1// Copyright 2024 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package release_config_lib 16 17import ( 18 "cmp" 19 "fmt" 20 "slices" 21 22 rc_proto "android/soong/cmd/release_config/release_config_proto" 23 24 "google.golang.org/protobuf/proto" 25) 26 27// A flag artifact, with its final value and declaration/override history. 28type FlagArtifact struct { 29 // The flag_declaration message. 30 FlagDeclaration *rc_proto.FlagDeclaration 31 32 // The index of the config directory where this flag was declared. 33 // Flag values cannot be set in a location with a lower index. 34 DeclarationIndex int 35 36 // A history of value assignments and overrides. 37 Traces []*rc_proto.Tracepoint 38 39 // The value of the flag. 40 Value *rc_proto.Value 41 42 // This flag is redacted. Set by UpdateValue when the FlagValue proto 43 // says to redact it. 44 Redacted bool 45} 46 47// Key is flag name. 48type FlagArtifacts map[string]*FlagArtifact 49 50func FlagArtifactFactory(declPath string) *FlagArtifact { 51 fd := &rc_proto.FlagDeclaration{} 52 fa := &FlagArtifact{ 53 FlagDeclaration: fd, 54 DeclarationIndex: -1, 55 Traces: []*rc_proto.Tracepoint{}, 56 } 57 if declPath != "" { 58 LoadMessage(declPath, fd) 59 fa.Value = fd.GetValue() 60 fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(declPath), Value: fa.Value}) 61 } 62 return fa 63} 64 65func FlagArtifactsFactory(artifactsPath string) *FlagArtifacts { 66 ret := make(FlagArtifacts) 67 if artifactsPath != "" { 68 fas := &rc_proto.FlagArtifacts{} 69 LoadMessage(artifactsPath, fas) 70 for _, fa_pb := range fas.Flags { 71 fa := &FlagArtifact{} 72 fa.FlagDeclaration = fa_pb.GetFlagDeclaration() 73 if val := fa_pb.GetValue(); val != nil { 74 fa.Value = val 75 } 76 if traces := fa_pb.GetTraces(); traces != nil { 77 fa.Traces = traces 78 } 79 ret[*fa.FlagDeclaration.Name] = fa 80 } 81 } 82 return &ret 83} 84 85func (fas *FlagArtifacts) SortedFlagNames() []string { 86 var names []string 87 for k, v := range *fas { 88 if !v.Redacted { 89 names = append(names, k) 90 } 91 } 92 slices.Sort(names) 93 return names 94} 95 96func (fa *FlagArtifact) GenerateFlagDeclarationArtifact() *rc_proto.FlagDeclarationArtifact { 97 ret := &rc_proto.FlagDeclarationArtifact{ 98 Name: fa.FlagDeclaration.Name, 99 DeclarationPath: fa.Traces[0].Source, 100 } 101 if namespace := fa.FlagDeclaration.GetNamespace(); namespace != "" { 102 ret.Namespace = proto.String(namespace) 103 } 104 if bugs := fa.FlagDeclaration.GetBugs(); bugs != nil { 105 ret.Bugs = bugs 106 } 107 if description := fa.FlagDeclaration.GetDescription(); description != "" { 108 ret.Description = proto.String(description) 109 } 110 if workflow := fa.FlagDeclaration.GetWorkflow(); workflow != rc_proto.Workflow_WORKFLOW_UNSPECIFIED { 111 ret.Workflow = &workflow 112 } 113 if containers := fa.FlagDeclaration.GetContainers(); containers != nil { 114 ret.Containers = containers 115 } 116 return ret 117} 118 119func FlagDeclarationArtifactsFactory(path string) *rc_proto.FlagDeclarationArtifacts { 120 ret := &rc_proto.FlagDeclarationArtifacts{} 121 if path != "" { 122 LoadMessage(path, ret) 123 } else { 124 ret.FlagDeclarationArtifactList = []*rc_proto.FlagDeclarationArtifact{} 125 } 126 return ret 127} 128 129func (fas *FlagArtifacts) GenerateFlagDeclarationArtifacts(intermediates []*rc_proto.FlagDeclarationArtifacts) *rc_proto.FlagDeclarationArtifacts { 130 ret := &rc_proto.FlagDeclarationArtifacts{FlagDeclarationArtifactList: []*rc_proto.FlagDeclarationArtifact{}} 131 for _, fa := range *fas { 132 ret.FlagDeclarationArtifactList = append(ret.FlagDeclarationArtifactList, fa.GenerateFlagDeclarationArtifact()) 133 } 134 for _, fda := range intermediates { 135 ret.FlagDeclarationArtifactList = append(ret.FlagDeclarationArtifactList, fda.FlagDeclarationArtifactList...) 136 } 137 slices.SortFunc(ret.FlagDeclarationArtifactList, func(a, b *rc_proto.FlagDeclarationArtifact) int { 138 return cmp.Compare(*a.Name, *b.Name) 139 }) 140 return ret 141} 142 143// Create a clone of the flag artifact. 144// 145// Returns: 146// 147// *FlagArtifact: the copy of the artifact. 148func (src *FlagArtifact) Clone() *FlagArtifact { 149 value := &rc_proto.Value{} 150 proto.Merge(value, src.Value) 151 return &FlagArtifact{ 152 FlagDeclaration: src.FlagDeclaration, 153 Traces: src.Traces, 154 Value: value, 155 DeclarationIndex: src.DeclarationIndex, 156 Redacted: src.Redacted, 157 } 158} 159 160// Clone FlagArtifacts. 161// 162// Returns: 163// 164// FlagArtifacts: a copy of the source FlagArtifacts. 165func (src FlagArtifacts) Clone() (dst FlagArtifacts) { 166 if dst == nil { 167 dst = make(FlagArtifacts) 168 } 169 for k, v := range src { 170 dst[k] = v.Clone() 171 } 172 return 173} 174 175// Update the value of a flag. 176// 177// This appends to flagArtifact.Traces, and updates flagArtifact.Value. 178// 179// Args: 180// 181// flagValue FlagValue: the value to assign 182// 183// Returns: 184// 185// error: any error encountered 186func (fa *FlagArtifact) UpdateValue(flagValue FlagValue) error { 187 name := *flagValue.proto.Name 188 redacted := flagValue.proto.GetRedacted() 189 if redacted { 190 fa.Redact() 191 flagValue.proto.Value = fa.Value 192 fmt.Printf("Redacting flag %s in %s\n", name, flagValue.path) 193 } else { 194 // If we are assigning a value, then the flag is no longer redacted. 195 fa.Redacted = false 196 } 197 fa.Traces = append(fa.Traces, &rc_proto.Tracepoint{Source: proto.String(flagValue.path), Value: flagValue.proto.Value}) 198 if fa.Value.GetObsolete() { 199 return fmt.Errorf("Attempting to set obsolete flag %s. Trace=%v", name, fa.Traces) 200 } 201 202 var newValue *rc_proto.Value 203 switch val := flagValue.proto.Value.Val.(type) { 204 case *rc_proto.Value_StringValue: 205 newValue = &rc_proto.Value{Val: &rc_proto.Value_StringValue{val.StringValue}} 206 case *rc_proto.Value_BoolValue: 207 newValue = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{val.BoolValue}} 208 case *rc_proto.Value_Obsolete: 209 if !val.Obsolete { 210 return fmt.Errorf("%s: Cannot set obsolete=false. Trace=%v", name, fa.Traces) 211 } 212 newValue = &rc_proto.Value{Val: &rc_proto.Value_Obsolete{true}} 213 default: 214 return fmt.Errorf("Invalid type for flag_value: %T. Trace=%v", val, fa.Traces) 215 } 216 if proto.Equal(newValue, fa.Value) { 217 warnf("%s: redundant override (set in %s)\n", flagValue.path, *fa.Traces[len(fa.Traces)-2].Source) 218 } 219 fa.Value = newValue 220 return nil 221} 222 223func (fa *FlagArtifact) Redact() { 224 fa.Redacted = true 225 fa.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{StringValue: "*REDACTED*"}} 226} 227 228// Marshal the FlagArtifact into a flag_artifact message. 229func (fa *FlagArtifact) Marshal() (*rc_proto.FlagArtifact, error) { 230 if fa.Redacted { 231 return nil, nil 232 } 233 return &rc_proto.FlagArtifact{ 234 FlagDeclaration: fa.FlagDeclaration, 235 Value: fa.Value, 236 Traces: fa.Traces, 237 }, nil 238} 239 240// Marshal the FlagArtifact without Traces. 241func (fa *FlagArtifact) MarshalWithoutTraces() (*rc_proto.FlagArtifact, error) { 242 if fa.Redacted { 243 return nil, nil 244 } 245 return &rc_proto.FlagArtifact{ 246 FlagDeclaration: fa.FlagDeclaration, 247 Value: fa.Value, 248 }, nil 249} 250