mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 10:03:42 +08:00 
			
		
		
		
	bake: implement composable attributes for attestations
Signed-off-by: Jonathan A. Sternberg <jonathan.sternberg@docker.com>
This commit is contained in:
		
							
								
								
									
										43
									
								
								bake/bake.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								bake/bake.go
									
									
									
									
									
								
							@@ -699,7 +699,7 @@ type Target struct {
 | 
				
			|||||||
	Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"`
 | 
						Inherits []string `json:"inherits,omitempty" hcl:"inherits,optional" cty:"inherits"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Annotations      []string                `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
 | 
						Annotations      []string                `json:"annotations,omitempty" hcl:"annotations,optional" cty:"annotations"`
 | 
				
			||||||
	Attest           []string                `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
 | 
						Attest           buildflags.Attests      `json:"attest,omitempty" hcl:"attest,optional" cty:"attest"`
 | 
				
			||||||
	Context          *string                 `json:"context,omitempty" hcl:"context,optional" cty:"context"`
 | 
						Context          *string                 `json:"context,omitempty" hcl:"context,optional" cty:"context"`
 | 
				
			||||||
	Contexts         map[string]string       `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
 | 
						Contexts         map[string]string       `json:"contexts,omitempty" hcl:"contexts,optional" cty:"contexts"`
 | 
				
			||||||
	Dockerfile       *string                 `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
 | 
						Dockerfile       *string                 `json:"dockerfile,omitempty" hcl:"dockerfile,optional" cty:"dockerfile"`
 | 
				
			||||||
@@ -718,8 +718,8 @@ type Target struct {
 | 
				
			|||||||
	NoCache          *bool                   `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
 | 
						NoCache          *bool                   `json:"no-cache,omitempty" hcl:"no-cache,optional" cty:"no-cache"`
 | 
				
			||||||
	NetworkMode      *string                 `json:"network,omitempty" hcl:"network,optional" cty:"network"`
 | 
						NetworkMode      *string                 `json:"network,omitempty" hcl:"network,optional" cty:"network"`
 | 
				
			||||||
	NoCacheFilter    []string                `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
 | 
						NoCacheFilter    []string                `json:"no-cache-filter,omitempty" hcl:"no-cache-filter,optional" cty:"no-cache-filter"`
 | 
				
			||||||
	ShmSize          *string                 `json:"shm-size,omitempty" hcl:"shm-size,optional"`
 | 
						ShmSize          *string                 `json:"shm-size,omitempty" hcl:"shm-size,optional" cty:"shm-size"`
 | 
				
			||||||
	Ulimits          []string                `json:"ulimits,omitempty" hcl:"ulimits,optional"`
 | 
						Ulimits          []string                `json:"ulimits,omitempty" hcl:"ulimits,optional" cty:"ulimits"`
 | 
				
			||||||
	Call             *string                 `json:"call,omitempty" hcl:"call,optional" cty:"call"`
 | 
						Call             *string                 `json:"call,omitempty" hcl:"call,optional" cty:"call"`
 | 
				
			||||||
	Entitlements     []string                `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
 | 
						Entitlements     []string                `json:"entitlements,omitempty" hcl:"entitlements,optional" cty:"entitlements"`
 | 
				
			||||||
	// IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md.
 | 
						// IMPORTANT: if you add more fields here, do not forget to update newOverrides/AddOverrides and docs/bake-reference.md.
 | 
				
			||||||
@@ -737,7 +737,7 @@ var (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (t *Target) normalize() {
 | 
					func (t *Target) normalize() {
 | 
				
			||||||
	t.Annotations = removeDupesStr(t.Annotations)
 | 
						t.Annotations = removeDupesStr(t.Annotations)
 | 
				
			||||||
	t.Attest = removeAttestDupes(t.Attest)
 | 
						t.Attest = t.Attest.Normalize()
 | 
				
			||||||
	t.Tags = removeDupesStr(t.Tags)
 | 
						t.Tags = removeDupesStr(t.Tags)
 | 
				
			||||||
	t.Secrets = t.Secrets.Normalize()
 | 
						t.Secrets = t.Secrets.Normalize()
 | 
				
			||||||
	t.SSH = t.SSH.Normalize()
 | 
						t.SSH = t.SSH.Normalize()
 | 
				
			||||||
@@ -811,8 +811,7 @@ func (t *Target) Merge(t2 *Target) {
 | 
				
			|||||||
		t.Annotations = append(t.Annotations, t2.Annotations...)
 | 
							t.Annotations = append(t.Annotations, t2.Annotations...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if t2.Attest != nil { // merge
 | 
						if t2.Attest != nil { // merge
 | 
				
			||||||
		t.Attest = append(t.Attest, t2.Attest...)
 | 
							t.Attest = t.Attest.Merge(t2.Attest)
 | 
				
			||||||
		t.Attest = removeAttestDupes(t.Attest)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if t2.Secrets != nil { // merge
 | 
						if t2.Secrets != nil { // merge
 | 
				
			||||||
		t.Secrets = t.Secrets.Merge(t2.Secrets)
 | 
							t.Secrets = t.Secrets.Merge(t2.Secrets)
 | 
				
			||||||
@@ -969,7 +968,11 @@ func (t *Target) AddOverrides(overrides map[string]Override, ent *EntitlementCon
 | 
				
			|||||||
		case "annotations":
 | 
							case "annotations":
 | 
				
			||||||
			t.Annotations = append(t.Annotations, o.ArrValue...)
 | 
								t.Annotations = append(t.Annotations, o.ArrValue...)
 | 
				
			||||||
		case "attest":
 | 
							case "attest":
 | 
				
			||||||
			t.Attest = append(t.Attest, o.ArrValue...)
 | 
								attest, err := parseArrValue[buildflags.Attest](o.ArrValue)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return errors.Wrap(err, "invalid value for attest")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								t.Attest = t.Attest.Merge(attest)
 | 
				
			||||||
		case "no-cache":
 | 
							case "no-cache":
 | 
				
			||||||
			noCache, err := strconv.ParseBool(value)
 | 
								noCache, err := strconv.ParseBool(value)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
@@ -1383,11 +1386,7 @@ func toBuildOpt(t *Target, inp *Input) (*build.Options, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	attests, err := buildflags.ParseAttests(t.Attest)
 | 
						bo.Attests = controllerapi.CreateAttestations(t.Attest.ToPB())
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	bo.Attests = controllerapi.CreateAttestations(attests)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bo.SourcePolicy, err = build.ReadSourcePolicy()
 | 
						bo.SourcePolicy, err = build.ReadSourcePolicy()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -1430,26 +1429,6 @@ func removeDupesStr(s []string) []string {
 | 
				
			|||||||
	return s[:i]
 | 
						return s[:i]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func removeAttestDupes(s []string) []string {
 | 
					 | 
				
			||||||
	res := []string{}
 | 
					 | 
				
			||||||
	m := map[string]int{}
 | 
					 | 
				
			||||||
	for _, v := range s {
 | 
					 | 
				
			||||||
		att, err := buildflags.ParseAttest(v)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			res = append(res, v)
 | 
					 | 
				
			||||||
			continue
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if i, ok := m[att.Type]; ok {
 | 
					 | 
				
			||||||
			res[i] = v
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			m[att.Type] = len(res)
 | 
					 | 
				
			||||||
			res = append(res, v)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return res
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func setPushOverride(outputs []*buildflags.ExportEntry, push bool) []*buildflags.ExportEntry {
 | 
					func setPushOverride(outputs []*buildflags.ExportEntry, push bool) []*buildflags.ExportEntry {
 | 
				
			||||||
	if !push {
 | 
						if !push {
 | 
				
			||||||
		// Disable push for any relevant export types
 | 
							// Disable push for any relevant export types
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1688,7 +1688,7 @@ func TestAttestDuplicates(t *testing.T) {
 | 
				
			|||||||
	ctx := context.TODO()
 | 
						ctx := context.TODO()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil, &EntitlementConf{})
 | 
						m, _, err := ReadTargets(ctx, []File{fp}, []string{"default"}, nil, nil, &EntitlementConf{})
 | 
				
			||||||
	require.Equal(t, []string{"type=sbom,foo=bar", "type=provenance,mode=max"}, m["default"].Attest)
 | 
						require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,foo=bar"}, stringify(m["default"].Attest))
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	opts, err := TargetsToBuildOpt(m, &Input{})
 | 
						opts, err := TargetsToBuildOpt(m, &Input{})
 | 
				
			||||||
@@ -1699,7 +1699,7 @@ func TestAttestDuplicates(t *testing.T) {
 | 
				
			|||||||
	}, opts["default"].Attests)
 | 
						}, opts["default"].Attests)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=true"}, nil, &EntitlementConf{})
 | 
						m, _, err = ReadTargets(ctx, []File{fp}, []string{"default"}, []string{"*.attest=type=sbom,disabled=true"}, nil, &EntitlementConf{})
 | 
				
			||||||
	require.Equal(t, []string{"type=sbom,disabled=true", "type=provenance,mode=max"}, m["default"].Attest)
 | 
						require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,disabled=true"}, stringify(m["default"].Attest))
 | 
				
			||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	opts, err = TargetsToBuildOpt(m, &Input{})
 | 
						opts, err = TargetsToBuildOpt(m, &Input{})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -604,6 +604,11 @@ func TestHCLAttrsCustomType(t *testing.T) {
 | 
				
			|||||||
func TestHCLAttrsCapsuleType(t *testing.T) {
 | 
					func TestHCLAttrsCapsuleType(t *testing.T) {
 | 
				
			||||||
	dt := []byte(`
 | 
						dt := []byte(`
 | 
				
			||||||
	target "app" {
 | 
						target "app" {
 | 
				
			||||||
 | 
							attest = [
 | 
				
			||||||
 | 
								{ type = "provenance", mode = "max" },
 | 
				
			||||||
 | 
								"type=sbom,disabled=true",
 | 
				
			||||||
 | 
							]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cache-from = [
 | 
							cache-from = [
 | 
				
			||||||
			{ type = "registry", ref = "user/app:cache" },
 | 
								{ type = "registry", ref = "user/app:cache" },
 | 
				
			||||||
			"type=local,src=path/to/cache",
 | 
								"type=local,src=path/to/cache",
 | 
				
			||||||
@@ -634,6 +639,7 @@ func TestHCLAttrsCapsuleType(t *testing.T) {
 | 
				
			|||||||
	require.NoError(t, err)
 | 
						require.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	require.Equal(t, 1, len(c.Targets))
 | 
						require.Equal(t, 1, len(c.Targets))
 | 
				
			||||||
 | 
						require.Equal(t, []string{"type=provenance,mode=max", "type=sbom,disabled=true"}, stringify(c.Targets[0].Attest))
 | 
				
			||||||
	require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(c.Targets[0].Outputs))
 | 
						require.Equal(t, []string{"type=local,dest=../out", "type=oci,dest=../out.tar"}, stringify(c.Targets[0].Outputs))
 | 
				
			||||||
	require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(c.Targets[0].CacheFrom))
 | 
						require.Equal(t, []string{"type=local,src=path/to/cache", "user/app:cache"}, stringify(c.Targets[0].CacheFrom))
 | 
				
			||||||
	require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo))
 | 
						require.Equal(t, []string{"type=local,dest=path/to/cache"}, stringify(c.Targets[0].CacheTo))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,9 @@
 | 
				
			|||||||
package buildflags
 | 
					package buildflags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"maps"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -10,6 +12,167 @@ import (
 | 
				
			|||||||
	"github.com/tonistiigi/go-csvvalue"
 | 
						"github.com/tonistiigi/go-csvvalue"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Attests []*Attest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a Attests) Merge(other Attests) Attests {
 | 
				
			||||||
 | 
						if other == nil {
 | 
				
			||||||
 | 
							a.Normalize()
 | 
				
			||||||
 | 
							return a
 | 
				
			||||||
 | 
						} else if a == nil {
 | 
				
			||||||
 | 
							other.Normalize()
 | 
				
			||||||
 | 
							return other
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return append(a, other...).Normalize()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a Attests) Normalize() Attests {
 | 
				
			||||||
 | 
						if len(a) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return removeAttestDupes(a)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a Attests) ToPB() []*controllerapi.Attest {
 | 
				
			||||||
 | 
						if len(a) == 0 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entries := make([]*controllerapi.Attest, len(a))
 | 
				
			||||||
 | 
						for i, entry := range a {
 | 
				
			||||||
 | 
							entries[i] = entry.ToPB()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return entries
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Attest struct {
 | 
				
			||||||
 | 
						Type     string            `json:"type"`
 | 
				
			||||||
 | 
						Disabled bool              `json:"disabled,omitempty"`
 | 
				
			||||||
 | 
						Attrs    map[string]string `json:"attrs,omitempty"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) Equal(other *Attest) bool {
 | 
				
			||||||
 | 
						if a.Type != other.Type || a.Disabled != other.Disabled {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return maps.Equal(a.Attrs, other.Attrs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) String() string {
 | 
				
			||||||
 | 
						var b csvBuilder
 | 
				
			||||||
 | 
						if a.Type != "" {
 | 
				
			||||||
 | 
							b.Write("type", a.Type)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.Disabled {
 | 
				
			||||||
 | 
							b.Write("disabled", "true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(a.Attrs) > 0 {
 | 
				
			||||||
 | 
							b.WriteAttributes(a.Attrs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.String()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) ToPB() *controllerapi.Attest {
 | 
				
			||||||
 | 
						var b csvBuilder
 | 
				
			||||||
 | 
						if a.Type != "" {
 | 
				
			||||||
 | 
							b.Write("type", a.Type)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if a.Disabled {
 | 
				
			||||||
 | 
							b.Write("disabled", "true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.WriteAttributes(a.Attrs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &controllerapi.Attest{
 | 
				
			||||||
 | 
							Type:     a.Type,
 | 
				
			||||||
 | 
							Disabled: a.Disabled,
 | 
				
			||||||
 | 
							Attrs:    b.String(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) MarshalJSON() ([]byte, error) {
 | 
				
			||||||
 | 
						m := make(map[string]interface{}, len(a.Attrs)+2)
 | 
				
			||||||
 | 
						for k, v := range m {
 | 
				
			||||||
 | 
							m[k] = v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m["type"] = a.Type
 | 
				
			||||||
 | 
						if a.Disabled {
 | 
				
			||||||
 | 
							m["disabled"] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return json.Marshal(m)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) UnmarshalJSON(data []byte) error {
 | 
				
			||||||
 | 
						var m map[string]interface{}
 | 
				
			||||||
 | 
						if err := json.Unmarshal(data, &m); err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if typ, ok := m["type"]; ok {
 | 
				
			||||||
 | 
							a.Type, ok = typ.(string)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return errors.Errorf("attest type must be a string")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							delete(m, "type")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if disabled, ok := m["disabled"]; ok {
 | 
				
			||||||
 | 
							a.Disabled, ok = disabled.(bool)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return errors.Errorf("attest disabled attribute must be a boolean")
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							delete(m, "disabled")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						attrs := make(map[string]string, len(m))
 | 
				
			||||||
 | 
						for k, v := range m {
 | 
				
			||||||
 | 
							s, ok := v.(string)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return errors.Errorf("attest attribute %q must be a string", k)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							attrs[k] = s
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						a.Attrs = attrs
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) UnmarshalText(text []byte) error {
 | 
				
			||||||
 | 
						in := string(text)
 | 
				
			||||||
 | 
						fields, err := csvvalue.Fields(in, nil)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						a.Attrs = map[string]string{}
 | 
				
			||||||
 | 
						for _, field := range fields {
 | 
				
			||||||
 | 
							key, value, ok := strings.Cut(field, "=")
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								return errors.Errorf("invalid value %s", field)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							key = strings.TrimSpace(strings.ToLower(key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							switch key {
 | 
				
			||||||
 | 
							case "type":
 | 
				
			||||||
 | 
								a.Type = value
 | 
				
			||||||
 | 
							case "disabled":
 | 
				
			||||||
 | 
								disabled, err := strconv.ParseBool(value)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return errors.Wrapf(err, "invalid value %s", field)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								a.Disabled = disabled
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								a.Attrs[key] = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return a.validate()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a *Attest) validate() error {
 | 
				
			||||||
 | 
						if a.Type == "" {
 | 
				
			||||||
 | 
							return errors.Errorf("attestation type not specified")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func CanonicalizeAttest(attestType string, in string) string {
 | 
					func CanonicalizeAttest(attestType string, in string) string {
 | 
				
			||||||
	if in == "" {
 | 
						if in == "" {
 | 
				
			||||||
		return ""
 | 
							return ""
 | 
				
			||||||
@@ -21,21 +184,34 @@ func CanonicalizeAttest(attestType string, in string) string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ParseAttests(in []string) ([]*controllerapi.Attest, error) {
 | 
					func ParseAttests(in []string) ([]*controllerapi.Attest, error) {
 | 
				
			||||||
	var out []*controllerapi.Attest
 | 
						var outs []*Attest
 | 
				
			||||||
	found := map[string]struct{}{}
 | 
						for _, s := range in {
 | 
				
			||||||
	for _, in := range in {
 | 
							var out Attest
 | 
				
			||||||
		in := in
 | 
							if err := out.UnmarshalText([]byte(s)); err != nil {
 | 
				
			||||||
		attest, err := ParseAttest(in)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							outs = append(outs, &out)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ConvertAttests(outs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ConvertAttests converts Attestations for the controller API from
 | 
				
			||||||
 | 
					// the ones in this package.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Attestations of the same type will cause an error. Some tools,
 | 
				
			||||||
 | 
					// like bake, remove the duplicates before calling this function.
 | 
				
			||||||
 | 
					func ConvertAttests(in []*Attest) ([]*controllerapi.Attest, error) {
 | 
				
			||||||
 | 
						out := make([]*controllerapi.Attest, 0, len(in))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check for dupplicate attestations while we convert them
 | 
				
			||||||
 | 
						// to the controller API.
 | 
				
			||||||
 | 
						found := map[string]struct{}{}
 | 
				
			||||||
 | 
						for _, attest := range in {
 | 
				
			||||||
		if _, ok := found[attest.Type]; ok {
 | 
							if _, ok := found[attest.Type]; ok {
 | 
				
			||||||
			return nil, errors.Errorf("duplicate attestation field %s", attest.Type)
 | 
								return nil, errors.Errorf("duplicate attestation field %s", attest.Type)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		found[attest.Type] = struct{}{}
 | 
							found[attest.Type] = struct{}{}
 | 
				
			||||||
 | 
							out = append(out, attest.ToPB())
 | 
				
			||||||
		out = append(out, attest)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return out, nil
 | 
						return out, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -77,3 +253,17 @@ func ParseAttest(in string) (*controllerapi.Attest, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	return &attest, nil
 | 
						return &attest, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func removeAttestDupes(s []*Attest) []*Attest {
 | 
				
			||||||
 | 
						res := []*Attest{}
 | 
				
			||||||
 | 
						m := map[string]int{}
 | 
				
			||||||
 | 
						for _, att := range s {
 | 
				
			||||||
 | 
							if i, ok := m[att.Type]; ok {
 | 
				
			||||||
 | 
								res[i] = att
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								m[att.Type] = len(res)
 | 
				
			||||||
 | 
								res = append(res, att)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return res
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										97
									
								
								util/buildflags/attests_cty.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								util/buildflags/attests_cty.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					package buildflags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/zclconf/go-cty/cty"
 | 
				
			||||||
 | 
						"github.com/zclconf/go-cty/cty/convert"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var attestType = sync.OnceValue(func() cty.Type {
 | 
				
			||||||
 | 
						return cty.Map(cty.String)
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *Attests) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
 | 
						got := in.Type()
 | 
				
			||||||
 | 
						if got.IsTupleType() || got.IsListType() {
 | 
				
			||||||
 | 
							return e.fromCtyValue(in, p)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						want := cty.List(attestType())
 | 
				
			||||||
 | 
						return p.NewErrorf("%s", convert.MismatchMessage(got, want))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *Attests) fromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
 | 
						*e = make([]*Attest, 0, in.LengthInt())
 | 
				
			||||||
 | 
						for elem := in.ElementIterator(); elem.Next(); {
 | 
				
			||||||
 | 
							_, value := elem.Element()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							entry := &Attest{}
 | 
				
			||||||
 | 
							if err := entry.FromCtyValue(value, p); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							*e = append(*e, entry)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e Attests) ToCtyValue() cty.Value {
 | 
				
			||||||
 | 
						if len(e) == 0 {
 | 
				
			||||||
 | 
							return cty.ListValEmpty(attestType())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vals := make([]cty.Value, len(e))
 | 
				
			||||||
 | 
						for i, entry := range e {
 | 
				
			||||||
 | 
							vals[i] = entry.ToCtyValue()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cty.ListVal(vals)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *Attest) FromCtyValue(in cty.Value, p cty.Path) error {
 | 
				
			||||||
 | 
						if in.Type() == cty.String {
 | 
				
			||||||
 | 
							if err := e.UnmarshalText([]byte(in.AsString())); err != nil {
 | 
				
			||||||
 | 
								return p.NewError(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conv, err := convert.Convert(in, cty.Map(cty.String))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						e.Attrs = map[string]string{}
 | 
				
			||||||
 | 
						for it := conv.ElementIterator(); it.Next(); {
 | 
				
			||||||
 | 
							k, v := it.Element()
 | 
				
			||||||
 | 
							switch key := k.AsString(); key {
 | 
				
			||||||
 | 
							case "type":
 | 
				
			||||||
 | 
								e.Type = v.AsString()
 | 
				
			||||||
 | 
							case "disabled":
 | 
				
			||||||
 | 
								b, err := strconv.ParseBool(v.AsString())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								e.Disabled = b
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								e.Attrs[key] = v.AsString()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (e *Attest) ToCtyValue() cty.Value {
 | 
				
			||||||
 | 
						if e == nil {
 | 
				
			||||||
 | 
							return cty.NullVal(cty.Map(cty.String))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						vals := make(map[string]cty.Value, len(e.Attrs)+2)
 | 
				
			||||||
 | 
						for k, v := range e.Attrs {
 | 
				
			||||||
 | 
							vals[k] = cty.StringVal(v)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						vals["type"] = cty.StringVal(e.Type)
 | 
				
			||||||
 | 
						if e.Disabled {
 | 
				
			||||||
 | 
							vals["disabled"] = cty.StringVal("true")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return cty.MapVal(vals)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user