xref: /aosp_15_r20/build/soong/cmd/release_config/release_config_lib/flag_artifact.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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