From b2ead03a7adfae7a3c6220255612069e1cd33989 Mon Sep 17 00:00:00 2001 From: Jon Johnson Date: Fri, 20 Dec 2024 12:10:30 -0800 Subject: [PATCH] Handle experiment tag and pointers in reflect See https://github.com/chainguard-dev/apko/pull/1453 Since GID is optional and 0 is a valid value, we needed a way to distinguish between unset and 0, so GID became a pointer. This broke the reflect code (labeled HACK) that punted on pointers. I've updated that code to handle pointers but also to ignore any fields marked with `apko:"experimental"` because I don't want to pollute the schema with them. Signed-off-by: Jon Johnson --- go.mod | 12 +++--- go.sum | 30 +++++++------- internal/provider/reflect.go | 79 +++++++++++++++++++++++++++++++----- 3 files changed, 91 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 79255f9f..21ddf04f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/chainguard-dev/terraform-provider-apko go 1.23.4 require ( - chainguard.dev/apko v0.22.3 + chainguard.dev/apko v0.22.4 github.com/chainguard-dev/clog v1.5.1 github.com/chainguard-dev/terraform-provider-oci v0.0.18 github.com/google/go-cmp v0.6.0 @@ -183,25 +183,25 @@ require ( go.opentelemetry.io/otel v1.33.0 // indirect go.opentelemetry.io/otel/metric v1.33.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect - go.step.sm/crypto v0.55.0 // indirect + go.step.sm/crypto v0.56.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.32.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/tools v0.26.0 // indirect - google.golang.org/api v0.213.0 // indirect + google.golang.org/api v0.214.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/grpc v1.67.1 // indirect - google.golang.org/protobuf v1.35.2 // indirect + google.golang.org/grpc v1.69.0 // indirect + google.golang.org/protobuf v1.36.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 53a254d9..c3c57dfd 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -chainguard.dev/apko v0.22.3 h1:av2SnPryS5YhR4rqjCv2gIRmrgitnsV9l3l9+sTp0m4= -chainguard.dev/apko v0.22.3/go.mod h1:r5NlHbveP9V6v6wk0j6SsQ4lLohNT4ypzpsSiml9ZHA= +chainguard.dev/apko v0.22.4 h1:DqLeqVijEMhFH6PhYYEry+3vPcd2DwHbV89MK3tw4e0= +chainguard.dev/apko v0.22.4/go.mod h1:QA+Bl4DgHNlDqpPUt9J2+JT+CaGtp2v1VyYNmwNgRq4= chainguard.dev/go-grpc-kit v0.17.6 h1:lwIs9LmSnm8jwrH1QmigCwMP6MYkIBENq/0xGduYZss= chainguard.dev/go-grpc-kit v0.17.6/go.mod h1:ZNaXn2KEO++2u2WveHs65krYiHmAEGjYLeEtfaQaOWU= chainguard.dev/sdk v0.1.28 h1:xLQv0JxiGhqVKOL059DmTReTjrKFhUsP5U1W6cgr+jQ= @@ -477,14 +477,16 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.29.0 h1:vkqKjk7gwhS8VaWb0POZKmIEDimRCMsopNYnriHyryo= -go.opentelemetry.io/otel/sdk v1.29.0/go.mod h1:pM8Dx5WKnvxLCb+8lG1PRNIDxu9g9b9g59Qr7hfAAok= +go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= +go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= +go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= +go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= -go.step.sm/crypto v0.55.0 h1:575Q7NahuM/ZRxUVN1GkO2e1aDYQJqIIg+nbfOajQJk= -go.step.sm/crypto v0.55.0/go.mod h1:MgEmD1lgwsuzZwTgI0GwKapHjKVEQLVggSvHuf3bYnU= +go.step.sm/crypto v0.56.0 h1:KcFfV76cI9Xaw8bdSc9x55skyuSdcHcTdL37vvVZnvY= +go.step.sm/crypto v0.56.0/go.mod h1:snWNloxY9s1W+HsFqcviq55nvzbqqX6LxVt0Vktv5mw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -530,8 +532,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= @@ -608,8 +610,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.213.0 h1:KmF6KaDyFqB417T68tMPbVmmwtIXs2VB60OJKIHB0xQ= -google.golang.org/api v0.213.0/go.mod h1:V0T5ZhNUUNpYAlL306gFZPFt5F5D/IeyLoktduYYnvQ= +google.golang.org/api v0.214.0 h1:h2Gkq07OYi6kusGOaT/9rnNljuXmqPnaig7WGPmKbwA= +google.golang.org/api v0.214.0/go.mod h1:bYPpLG8AyeMWwDU6NXoB00xC0DFkikVvd5MfwoxjLqE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= @@ -627,12 +629,12 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= +google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/provider/reflect.go b/internal/provider/reflect.go index 3b2a1e09..2c9aa6b8 100644 --- a/internal/provider/reflect.go +++ b/internal/provider/reflect.go @@ -2,7 +2,6 @@ package provider import ( "fmt" - "log" "reflect" "strings" @@ -58,14 +57,11 @@ func generateTypeReflect(t reflect.Type) (attr.Type, error) { if tag == nil { continue } - - // HACK: Handle this field. - if sf.Type.Kind() == reflect.Pointer { - log.Println("skipping pointer field", sf.Name) + if experimental(sf) { continue } - ft, err := generateTypeReflect(sf.Type) + ft, err := generateTypeReflect(maybeDeref(sf.Type)) if err != nil { return nil, fmt.Errorf("struct %w", err) } @@ -78,10 +74,46 @@ func generateTypeReflect(t reflect.Type) (attr.Type, error) { } } +func maybeDeref(t reflect.Type) reflect.Type { + if t.Kind() == reflect.Pointer { + // For pointers we want the element's type, not Pointer. + return t.Elem() + } + + return t +} + func generateValue(v any) (attr.Value, diag.Diagnostics) { return generateValueReflect(reflect.ValueOf(v)) } +func generateNull(t reflect.Type, at attr.Type) (attr.Value, diag.Diagnostics) { + switch t.Kind() { + case reflect.String: + return basetypes.NewStringNull(), nil + case reflect.Bool: + return basetypes.NewBoolNull(), nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return basetypes.NewInt64Null(), nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: + return basetypes.NewInt64Null(), nil + case reflect.Float32, reflect.Float64: + return basetypes.NewFloat64Null(), nil + case reflect.Array, reflect.Slice: + return basetypes.NewListNull(at), nil + case reflect.Map: + return basetypes.NewMapNull(at), nil + case reflect.Struct: + attrTyp, ok := at.(basetypes.ObjectType) + if !ok { + return nil, []diag.Diagnostic{diag.NewErrorDiagnostic("expected object type", "")} + } + return basetypes.NewObjectNull(attrTyp.AttrTypes), nil + default: + return nil, []diag.Diagnostic{diag.NewErrorDiagnostic("unexpected null type", t.Kind().String())} + } +} + func generateValueReflect(v reflect.Value) (attr.Value, diag.Diagnostics) { t := v.Type() switch t.Kind() { @@ -142,10 +174,20 @@ func generateValueReflect(v reflect.Value) (attr.Value, diag.Diagnostics) { if tag == nil { continue } + if experimental(sf) { + continue + } - // HACK: Handle this field. - if sf.Type.Kind() == reflect.Pointer { - log.Println("skipping pointer field", sf.Name) + if sf.Type.Kind() == reflect.Pointer && v.Field(i).IsNil() { + at, err := generateTypeReflect(sf.Type) + if err != nil { + return nil, []diag.Diagnostic{diag.NewErrorDiagnostic(err.Error(), "")} + } + ft, diags := generateNull(sf.Type.Elem(), at) + if diags.HasError() { + return nil, diags + } + fv[*tag] = ft continue } @@ -298,11 +340,15 @@ func assignValueReflect(in attr.Value, out reflect.Value) diag.Diagnostics { if tag == nil { continue } + if experimental(sf) { + continue + } val, ok := fl[*tag] if !ok { continue } - diags := assignValueReflect(val, out.Field(i)) + + diags := assignValueReflect(val, indirect(out.Field(i))) if diags.HasError() { return diags } @@ -336,6 +382,19 @@ func yamlName(field reflect.StructField) *string { return &fn } +func experimental(field reflect.StructField) bool { + tag := field.Tag.Get("apko") + if tag == "" { + return false + } + for _, field := range strings.Split(tag, ",") { + if field == "experimental" { + return true + } + } + return false +} + // indirect walks down v allocating pointers as needed, // until it gets to a non-pointer. // If it encounters an Unmarshaler, indirect stops and returns that.