diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index b85a2393..77f4409a 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,13 +1,13 @@ ack_generate_info: - build_date: "2022-09-12T19:48:02Z" - build_hash: 2944c8772f216656d84ee02d392eaca501274c1e - go_version: go1.17.5 - version: v0.20.1 -api_directory_checksum: 944b0a250ed0cea5848ac5724812128f74177e8b + build_date: "2022-10-12T20:02:48Z" + build_hash: 5ee0ac052c54f008dff50f6f5ebb73f2cf3a0bd7 + go_version: go1.19 + version: v0.19.3-11-g5ee0ac0-dirty +api_directory_checksum: e20f002cfa1d08ee4f7ff59aa4011e6c538b5836 api_version: v1alpha1 aws_sdk_go_version: v1.44.93 generator_config_info: - file_checksum: 5ad03a60a59749711f687c6ffb7b01a5bf7f28e2 + file_checksum: 0b802e2a5f0169c37248fd40a259ab988a3714df original_file_name: generator.yaml last_modification: reason: API generation diff --git a/apis/v1alpha1/function.go b/apis/v1alpha1/function.go index 6701bf16..f0d5a737 100644 --- a/apis/v1alpha1/function.go +++ b/apis/v1alpha1/function.go @@ -58,7 +58,8 @@ type FunctionSpec struct { // The ARN of the Amazon Web Services Key Management Service (KMS) key that's // used to encrypt your function's environment variables. If it's not provided, // Lambda uses a default service key. - KMSKeyARN *string `json:"kmsKeyARN,omitempty"` + KMSKeyARN *string `json:"kmsKeyARN,omitempty"` + KMSKeyRef *ackv1alpha1.AWSResourceReferenceWrapper `json:"kmsKeyRef,omitempty"` // A list of function layers (https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) // to add to the function's execution environment. Specify each layer by its // ARN, including the version. diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index a04d7329..39205a80 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -8,6 +8,11 @@ ignore: resources: Function: fields: + KMSKeyARN: + references: + resource: Key + path: Status.ACKResourceMetadata.ARN + service_name: kms Name: is_primary_key: true is_required: true diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index 3c2ff748..9c7cad92 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -1691,6 +1691,11 @@ func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) { *out = new(string) **out = **in } + if in.KMSKeyRef != nil { + in, out := &in.KMSKeyRef, &out.KMSKeyRef + *out = new(corev1alpha1.AWSResourceReferenceWrapper) + (*in).DeepCopyInto(*out) + } if in.Layers != nil { in, out := &in.Layers, &out.Layers *out = make([]*string, len(*in)) diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 22151ddd..aca18a3f 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -18,6 +18,7 @@ package main import ( "os" + kmsapitypes "github.com/aws-controllers-k8s/kms-controller/apis/v1alpha1" ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" ackcfg "github.com/aws-controllers-k8s/runtime/pkg/config" ackrt "github.com/aws-controllers-k8s/runtime/pkg/runtime" @@ -56,6 +57,7 @@ func init() { _ = svctypes.AddToScheme(scheme) _ = ackv1alpha1.AddToScheme(scheme) + _ = kmsapitypes.AddToScheme(scheme) } func main() { diff --git a/config/crd/bases/lambda.services.k8s.aws_eventsourcemappings.yaml b/config/crd/bases/lambda.services.k8s.aws_eventsourcemappings.yaml index 2e41ffd0..9ed4bff3 100644 --- a/config/crd/bases/lambda.services.k8s.aws_eventsourcemappings.yaml +++ b/config/crd/bases/lambda.services.k8s.aws_eventsourcemappings.yaml @@ -52,7 +52,7 @@ spec: Streams - Default 100. Max 10,000. \n * Amazon Simple Queue Service - Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10. \n * Amazon Managed Streaming for Apache Kafka - - Default 100. Max 10,000. \n * Self-Managed Apache Kafka - Default + Default 100. Max 10,000. \n * Self-managed Apache Kafka - Default 100. Max 10,000. \n * Amazon MQ (ActiveMQ and RabbitMQ) - Default 100. Max 10,000." format: int64 diff --git a/config/crd/bases/lambda.services.k8s.aws_functions.yaml b/config/crd/bases/lambda.services.k8s.aws_functions.yaml index 19d5f77c..f09dbbe6 100644 --- a/config/crd/bases/lambda.services.k8s.aws_functions.yaml +++ b/config/crd/bases/lambda.services.k8s.aws_functions.yaml @@ -131,6 +131,19 @@ spec: (KMS) key that's used to encrypt your function's environment variables. If it's not provided, Lambda uses a default service key. type: string + kmsKeyRef: + description: 'AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference type to provide more user friendly syntax + for references using ''from'' field Ex: APIIDRef: from: name: my-api' + properties: + from: + description: AWSResourceReference provides all the values necessary + to reference another k8s resource for finding the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object layers: description: A list of function layers (https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) to add to the function's execution environment. Specify each layer diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index 28c3cd24..05e896bf 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -31,6 +31,20 @@ rules: - list - patch - watch +- apiGroups: + - kms.services.k8s.aws + resources: + - keys + verbs: + - get + - list +- apiGroups: + - kms.services.k8s.aws + resources: + - keys/status + verbs: + - get + - list - apiGroups: - lambda.services.k8s.aws resources: diff --git a/generator.yaml b/generator.yaml index a04d7329..39205a80 100644 --- a/generator.yaml +++ b/generator.yaml @@ -8,6 +8,11 @@ ignore: resources: Function: fields: + KMSKeyARN: + references: + resource: Key + path: Status.ACKResourceMetadata.ARN + service_name: kms Name: is_primary_key: true is_required: true diff --git a/go.mod b/go.mod index a55a32cd..345c2942 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/aws-controllers-k8s/lambda-controller go 1.17 require ( + github.com/aws-controllers-k8s/kms-controller v0.1.2 github.com/aws-controllers-k8s/runtime v0.20.1 github.com/aws/aws-sdk-go v1.44.93 github.com/go-logr/logr v1.2.0 @@ -42,7 +43,6 @@ require ( github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.28.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect - github.com/stretchr/objx v0.2.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.19.1 // indirect diff --git a/go.sum b/go.sum index 3a9fb7a0..f44e198f 100644 --- a/go.sum +++ b/go.sum @@ -64,6 +64,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws-controllers-k8s/kms-controller v0.1.2 h1:9lb98jspqOpFpmIFHOJ6pRnOkC8kDEPIgTAb5QnVGZo= +github.com/aws-controllers-k8s/kms-controller v0.1.2/go.mod h1:6CoV0UMFd03EUF9dXgOTTScGdBhJzsWn9W0dw2n0kA4= github.com/aws-controllers-k8s/runtime v0.20.1 h1:L/Huf1shRahx5BqJBCSS5u+vYg3f0Rotsq1jutORpdI= github.com/aws-controllers-k8s/runtime v0.20.1/go.mod h1:k7z4qlf6aK1Kzd4ff49wzcyhDKHjWaUpqxrwgl4uS1o= github.com/aws/aws-sdk-go v1.44.93 h1:hAgd9fuaptBatSft27/5eBMdcA8+cIMqo96/tZ6rKl8= diff --git a/helm/crds/lambda.services.k8s.aws_eventsourcemappings.yaml b/helm/crds/lambda.services.k8s.aws_eventsourcemappings.yaml index 2e41ffd0..9ed4bff3 100644 --- a/helm/crds/lambda.services.k8s.aws_eventsourcemappings.yaml +++ b/helm/crds/lambda.services.k8s.aws_eventsourcemappings.yaml @@ -52,7 +52,7 @@ spec: Streams - Default 100. Max 10,000. \n * Amazon Simple Queue Service - Default 10. For standard queues the max is 10,000. For FIFO queues the max is 10. \n * Amazon Managed Streaming for Apache Kafka - - Default 100. Max 10,000. \n * Self-Managed Apache Kafka - Default + Default 100. Max 10,000. \n * Self-managed Apache Kafka - Default 100. Max 10,000. \n * Amazon MQ (ActiveMQ and RabbitMQ) - Default 100. Max 10,000." format: int64 diff --git a/helm/crds/lambda.services.k8s.aws_functions.yaml b/helm/crds/lambda.services.k8s.aws_functions.yaml index 19d5f77c..f09dbbe6 100644 --- a/helm/crds/lambda.services.k8s.aws_functions.yaml +++ b/helm/crds/lambda.services.k8s.aws_functions.yaml @@ -131,6 +131,19 @@ spec: (KMS) key that's used to encrypt your function's environment variables. If it's not provided, Lambda uses a default service key. type: string + kmsKeyRef: + description: 'AWSResourceReferenceWrapper provides a wrapper around + *AWSResourceReference type to provide more user friendly syntax + for references using ''from'' field Ex: APIIDRef: from: name: my-api' + properties: + from: + description: AWSResourceReference provides all the values necessary + to reference another k8s resource for finding the identifier(Id/ARN/Name) + properties: + name: + type: string + type: object + type: object layers: description: A list of function layers (https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html) to add to the function's execution environment. Specify each layer diff --git a/helm/templates/cluster-role-controller.yaml b/helm/templates/cluster-role-controller.yaml index 0ca4ec95..12ec40c7 100644 --- a/helm/templates/cluster-role-controller.yaml +++ b/helm/templates/cluster-role-controller.yaml @@ -4,11 +4,19 @@ kind: ClusterRole metadata: creationTimestamp: null name: ack-lambda-controller + labels: + {{- range $key, $value := .Values.role.labels }} + {{ $key }}: {{ $value | quote }} + {{- end }} {{ else }} kind: Role metadata: creationTimestamp: null name: ack-lambda-controller + labels: + {{- range $key, $value := .Values.role.labels }} + {{ $key }}: {{ $value | quote }} + {{- end }} namespace: {{ .Release.Namespace }} {{ end }} rules: @@ -38,6 +46,20 @@ rules: - list - patch - watch +- apiGroups: + - kms.services.k8s.aws + resources: + - keys + verbs: + - get + - list +- apiGroups: + - kms.services.k8s.aws + resources: + - keys/status + verbs: + - get + - list - apiGroups: - lambda.services.k8s.aws resources: diff --git a/helm/values.schema.json b/helm/values.schema.json index 2d3555a6..86a7580f 100644 --- a/helm/values.schema.json +++ b/helm/values.schema.json @@ -65,6 +65,14 @@ ], "type": "object" }, + "role": { + "description": "Role settings", + "properties": { + "labels": { + "type": "object" + } + } + }, "metrics": { "description": "Metrics settings", "properties": { diff --git a/helm/values.yaml b/helm/values.yaml index be1cb13e..4dfa129c 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -28,6 +28,10 @@ deployment: # Which priorityClassName to set? # See: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/#pod-priority priorityClassName: "" + +# If "installScope: cluster" then these labels will be applied to ClusterRole +role: + labels: {} metrics: service: diff --git a/pkg/resource/function/delta.go b/pkg/resource/function/delta.go index 6881b0f5..47487310 100644 --- a/pkg/resource/function/delta.go +++ b/pkg/resource/function/delta.go @@ -126,6 +126,9 @@ func newResourceDelta( delta.Add("Spec.KMSKeyARN", a.ko.Spec.KMSKeyARN, b.ko.Spec.KMSKeyARN) } } + if !reflect.DeepEqual(a.ko.Spec.KMSKeyRef, b.ko.Spec.KMSKeyRef) { + delta.Add("Spec.KMSKeyRef", a.ko.Spec.KMSKeyRef, b.ko.Spec.KMSKeyRef) + } if !ackcompare.SliceStringPEqual(a.ko.Spec.Layers, b.ko.Spec.Layers) { delta.Add("Spec.Layers", a.ko.Spec.Layers, b.ko.Spec.Layers) } diff --git a/pkg/resource/function/references.go b/pkg/resource/function/references.go index e4b9a3f1..06e595da 100644 --- a/pkg/resource/function/references.go +++ b/pkg/resource/function/references.go @@ -17,13 +17,24 @@ package function import ( "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + kmsapitypes "github.com/aws-controllers-k8s/kms-controller/apis/v1alpha1" + ackv1alpha1 "github.com/aws-controllers-k8s/runtime/apis/core/v1alpha1" + ackcondition "github.com/aws-controllers-k8s/runtime/pkg/condition" + ackerr "github.com/aws-controllers-k8s/runtime/pkg/errors" acktypes "github.com/aws-controllers-k8s/runtime/pkg/types" svcapitypes "github.com/aws-controllers-k8s/lambda-controller/apis/v1alpha1" ) +// +kubebuilder:rbac:groups=kms.services.k8s.aws,resources=keys,verbs=get;list +// +kubebuilder:rbac:groups=kms.services.k8s.aws,resources=keys/status,verbs=get;list + // ResolveReferences finds if there are any Reference field(s) present // inside AWSResource passed in the parameter and attempts to resolve // those reference field(s) into target field(s). @@ -36,17 +47,92 @@ func (rm *resourceManager) ResolveReferences( apiReader client.Reader, res acktypes.AWSResource, ) (acktypes.AWSResource, error) { - return res, nil + namespace := res.MetaObject().GetNamespace() + ko := rm.concreteResource(res).ko.DeepCopy() + err := validateReferenceFields(ko) + if err == nil { + err = resolveReferenceForKMSKeyARN(ctx, apiReader, namespace, ko) + } + + // If there was an error while resolving any reference, reset all the + // resolved values so that they do not get persisted inside etcd + if err != nil { + ko = rm.concreteResource(res).ko.DeepCopy() + } + if hasNonNilReferences(ko) { + return ackcondition.WithReferencesResolvedCondition(&resource{ko}, err) + } + return &resource{ko}, err } // validateReferenceFields validates the reference field and corresponding // identifier field. func validateReferenceFields(ko *svcapitypes.Function) error { + if ko.Spec.KMSKeyRef != nil && ko.Spec.KMSKeyARN != nil { + return ackerr.ResourceReferenceAndIDNotSupportedFor("KMSKeyARN", "KMSKeyRef") + } return nil } // hasNonNilReferences returns true if resource contains a reference to another // resource func hasNonNilReferences(ko *svcapitypes.Function) bool { - return false + return false || (ko.Spec.KMSKeyRef != nil) +} + +// resolveReferenceForKMSKeyARN reads the resource referenced +// from KMSKeyRef field and sets the KMSKeyARN +// from referenced resource +func resolveReferenceForKMSKeyARN( + ctx context.Context, + apiReader client.Reader, + namespace string, + ko *svcapitypes.Function, +) error { + if ko.Spec.KMSKeyRef != nil && + ko.Spec.KMSKeyRef.From != nil { + arr := ko.Spec.KMSKeyRef.From + if arr == nil || arr.Name == nil || *arr.Name == "" { + return fmt.Errorf("provided resource reference is nil or empty") + } + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: *arr.Name, + } + obj := kmsapitypes.Key{} + err := apiReader.Get(ctx, namespacedName, &obj) + if err != nil { + return err + } + var refResourceSynced, refResourceTerminal bool + for _, cond := range obj.Status.Conditions { + if cond.Type == ackv1alpha1.ConditionTypeResourceSynced && + cond.Status == corev1.ConditionTrue { + refResourceSynced = true + } + if cond.Type == ackv1alpha1.ConditionTypeTerminal && + cond.Status == corev1.ConditionTrue { + refResourceTerminal = true + } + } + if refResourceTerminal { + return ackerr.ResourceReferenceTerminalFor( + "Key", + namespace, *arr.Name) + } + if !refResourceSynced { + return ackerr.ResourceReferenceNotSyncedFor( + "Key", + namespace, *arr.Name) + } + if obj.Status.ACKResourceMetadata == nil || obj.Status.ACKResourceMetadata.ARN == nil { + return ackerr.ResourceReferenceMissingTargetFieldFor( + "Key", + namespace, *arr.Name, + "Status.ACKResourceMetadata.ARN") + } + referencedValue := string(*obj.Status.ACKResourceMetadata.ARN) + ko.Spec.KMSKeyARN = &referencedValue + } + return nil }