diff --git a/apis/v1alpha1/ack-generate-metadata.yaml b/apis/v1alpha1/ack-generate-metadata.yaml index 3d9f37f..86edcd7 100755 --- a/apis/v1alpha1/ack-generate-metadata.yaml +++ b/apis/v1alpha1/ack-generate-metadata.yaml @@ -1,14 +1,14 @@ ack_generate_info: - build_date: "2021-08-12T22:22:41Z" + build_date: "2021-08-12T23:12:20Z" build_hash: 4d0db1b6f794e5221eb88b052b52a1a95017cf20 go_version: go1.15.6 linux/amd64 version: v0.9.2 -api_directory_checksum: a111b8798ffa13b2cf948befafae28feaef463ab +api_directory_checksum: 0d04fca79a5350fa289a4aac8afd284351778233 api_version: v1alpha1 aws_sdk_go_version: v1.37.10 generator_config_info: - file_checksum: 85625c1a8af87eb3bbda9c6764a356bd3751cb31 + file_checksum: b3cfb60b1b7154bc50d4c28096dfc75bf35a3767 original_file_name: generator.yaml last_modification: reason: API generation - timestamp: 2021-08-12 22:22:48.65031987 +0000 UTC + timestamp: 2021-08-12 23:12:25.389076653 +0000 UTC diff --git a/apis/v1alpha1/bucket.go b/apis/v1alpha1/bucket.go index 31af670..4c5e4de 100644 --- a/apis/v1alpha1/bucket.go +++ b/apis/v1alpha1/bucket.go @@ -59,6 +59,8 @@ type BucketSpec struct { // The OwnershipControls (BucketOwnerPreferred or ObjectWriter) that you want // to apply to this Amazon S3 bucket. OwnershipControls *OwnershipControls `json:"ownershipControls,omitempty"` + // The bucket policy as a JSON document. + Policy *string `json:"policy,omitempty"` // Container for Payer. RequestPayment *RequestPaymentConfiguration `json:"requestPayment,omitempty"` // Container for the TagSet and Tag elements. diff --git a/apis/v1alpha1/generator.yaml b/apis/v1alpha1/generator.yaml index 1952987..43afd9e 100644 --- a/apis/v1alpha1/generator.yaml +++ b/apis/v1alpha1/generator.yaml @@ -58,10 +58,10 @@ resources: from: operation: PutBucketOwnershipControls path: OwnershipControls - # Policy: - # from: - # operation: PutBucketPolicy - # path: Policy # Double check about ConfirmRemoveSelfBucketAccess + Policy: + from: + operation: PutBucketPolicy + path: Policy # Double check about ConfirmRemoveSelfBucketAccess # Replication: # from: # operation: PutBucketReplication diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go index ee7239e..eff66ac 100644 --- a/apis/v1alpha1/zz_generated.deepcopy.go +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -287,6 +287,11 @@ func (in *BucketSpec) DeepCopyInto(out *BucketSpec) { *out = new(OwnershipControls) (*in).DeepCopyInto(*out) } + if in.Policy != nil { + in, out := &in.Policy, &out.Policy + *out = new(string) + **out = **in + } if in.RequestPayment != nil { in, out := &in.RequestPayment, &out.RequestPayment *out = new(RequestPaymentConfiguration) diff --git a/config/crd/bases/s3.services.k8s.aws_buckets.yaml b/config/crd/bases/s3.services.k8s.aws_buckets.yaml index 8d3a6d9..a518708 100644 --- a/config/crd/bases/s3.services.k8s.aws_buckets.yaml +++ b/config/crd/bases/s3.services.k8s.aws_buckets.yaml @@ -194,6 +194,9 @@ spec: type: object type: array type: object + policy: + description: The bucket policy as a JSON document. + type: string requestPayment: description: Container for Payer. properties: diff --git a/generator.yaml b/generator.yaml index 1952987..43afd9e 100644 --- a/generator.yaml +++ b/generator.yaml @@ -58,10 +58,10 @@ resources: from: operation: PutBucketOwnershipControls path: OwnershipControls - # Policy: - # from: - # operation: PutBucketPolicy - # path: Policy # Double check about ConfirmRemoveSelfBucketAccess + Policy: + from: + operation: PutBucketPolicy + path: Policy # Double check about ConfirmRemoveSelfBucketAccess # Replication: # from: # operation: PutBucketReplication diff --git a/helm/Chart.yaml b/helm/Chart.yaml index 9dbd784..a700433 100644 --- a/helm/Chart.yaml +++ b/helm/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v1 -name: ack-s3-controller +name: s3-chart description: A Helm chart for the ACK service controller for s3 version: v0.0.2 appVersion: v0.0.2 @@ -10,7 +10,7 @@ sources: maintainers: - name: ACK Admins url: https://github.com/orgs/aws-controllers-k8s/teams/ack-admin - - name: S3 Admins + - name: s3 Admins url: https://github.com/orgs/aws-controllers-k8s/teams/s3-maintainer keywords: - aws diff --git a/helm/crds/s3.services.k8s.aws_buckets.yaml b/helm/crds/s3.services.k8s.aws_buckets.yaml index de72c48..a518708 100644 --- a/helm/crds/s3.services.k8s.aws_buckets.yaml +++ b/helm/crds/s3.services.k8s.aws_buckets.yaml @@ -4,7 +4,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.4.0 + controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null name: buckets.s3.services.k8s.aws spec: @@ -34,29 +34,258 @@ spec: metadata: type: object spec: - description: BucketSpec defines the desired state of Bucket + description: "BucketSpec defines the desired state of Bucket. \n In terms + of implementation, a Bucket is a resource. An Amazon S3 bucket name + is globally unique, and the namespace is shared by all AWS accounts." properties: + accelerate: + description: Container for setting the transfer acceleration state. + properties: + status: + type: string + type: object acl: + description: The canned ACL to apply to the bucket. type: string + cors: + description: Describes the cross-origin access configuration for objects + in an Amazon S3 bucket. For more information, see Enabling Cross-Origin + Resource Sharing (https://docs.aws.amazon.com/AmazonS3/latest/dev/cors.html) + in the Amazon Simple Storage Service Developer Guide. + properties: + corsRules: + items: + description: Specifies a cross-origin access rule for an Amazon + S3 bucket. + properties: + allowedHeaders: + items: + type: string + type: array + allowedMethods: + items: + type: string + type: array + allowedOrigins: + items: + type: string + type: array + exposeHeaders: + items: + type: string + type: array + maxAgeSeconds: + format: int64 + type: integer + type: object + type: array + type: object createBucketConfiguration: + description: The configuration information for the bucket. properties: locationConstraint: type: string type: object + encryption: + description: Specifies the default server-side-encryption configuration. + properties: + rules: + items: + description: Specifies the default server-side encryption configuration. + properties: + applyServerSideEncryptionByDefault: + description: Describes the default server-side encryption + to apply to new objects in the bucket. If a PUT Object + request doesn't specify any server-side encryption, this + default encryption will be applied. For more information, + see PUT Bucket encryption (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTencryption.html) + in the Amazon Simple Storage Service API Reference. + properties: + kmsMasterKeyID: + type: string + sseAlgorithm: + type: string + type: object + bucketKeyEnabled: + type: boolean + type: object + type: array + type: object grantFullControl: + description: Allows grantee the read, write, read ACP, and write ACP + permissions on the bucket. type: string grantRead: + description: Allows grantee to list the objects in the bucket. type: string grantReadACP: + description: Allows grantee to read the bucket ACL. type: string grantWrite: + description: Allows grantee to create, overwrite, and delete any object + in the bucket. type: string grantWriteACP: + description: Allows grantee to write the ACL for the applicable bucket. type: string + logging: + description: Container for logging status information. + properties: + loggingEnabled: + description: Describes where logs are stored and the prefix that + Amazon S3 assigns to all log object keys for a bucket. For more + information, see PUT Bucket logging (https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTlogging.html) + in the Amazon Simple Storage Service API Reference. + properties: + targetBucket: + type: string + targetGrants: + items: + description: Container for granting information. + properties: + grantee: + description: Container for the person being granted + permissions. + properties: + displayName: + type: string + emailAddress: + type: string + id: + type: string + type_: + type: string + uRI: + type: string + type: object + permission: + type: string + type: object + type: array + targetPrefix: + type: string + type: object + type: object name: + description: The name of the bucket to create. type: string objectLockEnabledForBucket: + description: Specifies whether you want S3 Object Lock to be enabled + for the new bucket. type: boolean + ownershipControls: + description: The OwnershipControls (BucketOwnerPreferred or ObjectWriter) + that you want to apply to this Amazon S3 bucket. + properties: + rules: + items: + description: The container element for an ownership control + rule. + properties: + objectOwnership: + description: "The container element for object ownership + for a bucket's ownership controls. \n BucketOwnerPreferred + - Objects uploaded to the bucket change ownership to the + bucket owner if the objects are uploaded with the bucket-owner-full-control + canned ACL. \n ObjectWriter - The uploading account will + own the object if the object is uploaded with the bucket-owner-full-control + canned ACL." + type: string + type: object + type: array + type: object + policy: + description: The bucket policy as a JSON document. + type: string + requestPayment: + description: Container for Payer. + properties: + payer: + type: string + type: object + tagging: + description: Container for the TagSet and Tag elements. + properties: + tagSet: + items: + description: A container of a key value name pair. + properties: + key: + type: string + value: + type: string + type: object + type: array + type: object + versioning: + description: Container for setting the versioning state. + properties: + status: + type: string + type: object + website: + description: Container for the request. + properties: + errorDocument: + description: The error information. + properties: + key: + type: string + type: object + indexDocument: + description: Container for the Suffix element. + properties: + suffix: + type: string + type: object + redirectAllRequestsTo: + description: Specifies the redirect behavior of all requests to + a website endpoint of an Amazon S3 bucket. + properties: + hostName: + type: string + protocol: + type: string + type: object + routingRules: + items: + description: Specifies the redirect behavior and when a redirect + is applied. For more information about routing rules, see + Configuring advanced conditional redirects (https://docs.aws.amazon.com/AmazonS3/latest/dev/how-to-page-redirect.html#advanced-conditional-redirects) + in the Amazon Simple Storage Service Developer Guide. + properties: + condition: + description: A container for describing a condition that + must be met for the specified redirect to apply. For example, + 1. If request is for pages in the /docs folder, redirect + to the /documents folder. 2. If request results in HTTP + error 4xx, redirect request to another host where you + might process the error. + properties: + httpErrorCodeReturnedEquals: + type: string + keyPrefixEquals: + type: string + type: object + redirect: + description: Specifies how requests are redirected. In the + event of an error, you can specify a different error code + to return. + properties: + hostName: + type: string + httpRedirectCode: + type: string + protocol: + type: string + replaceKeyPrefixWith: + type: string + replaceKeyWith: + type: string + type: object + type: object + type: array + type: object required: - name type: object @@ -120,10 +349,10 @@ spec: type: object type: array location: + description: Specifies the Region where the bucket will be created. + If you are creating a bucket on the US East (N. Virginia) Region + (us-east-1), you do not need to specify the location. type: string - required: - - ackResourceMetadata - - conditions type: object type: object served: true diff --git a/helm/crds/services.k8s.aws_adoptedresources.yaml b/helm/crds/services.k8s.aws_adoptedresources.yaml new file mode 100644 index 0000000..c1b2484 --- /dev/null +++ b/helm/crds/services.k8s.aws_adoptedresources.yaml @@ -0,0 +1,233 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.6.1 + creationTimestamp: null + name: adoptedresources.services.k8s.aws +spec: + group: services.k8s.aws + names: + kind: AdoptedResource + listKind: AdoptedResourceList + plural: adoptedresources + singular: adoptedresource + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: AdoptedResource is the schema for the AdoptedResource API. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: AdoptedResourceSpec defines the desired state of the AdoptedResource. + properties: + aws: + description: AWSIdentifiers provide all unique ways to reference an + AWS resource. + properties: + additionalKeys: + additionalProperties: + type: string + description: AdditionalKeys represents any additional arbitrary + identifiers used when describing the target resource. + type: object + arn: + description: ARN is the AWS Resource Name for the resource. It + is a globally unique identifier. + type: string + nameOrID: + description: NameOrId is a user-supplied string identifier for + the resource. It may or may not be globally unique, depending + on the type of resource. + type: string + type: object + kubernetes: + description: TargetKubernetesResource provides all the values necessary + to identify a given ACK type and override any metadata values when + creating a resource of that type. + properties: + group: + type: string + kind: + type: string + metadata: + description: "ObjectMeta is metadata that all persisted resources + must have, which includes all objects users must create. It + is not possible to use `metav1.ObjectMeta` inside spec, as the + controller-gen automatically converts this to an arbitrary string-string + map. https://github.com/kubernetes-sigs/controller-tools/issues/385 + \n Active discussion about inclusion of this field in the spec + is happening in this PR: https://github.com/kubernetes-sigs/controller-tools/pull/395 + \n Until this is allowed, or if it never is, we will produce + a subset of the object meta that contains only the fields which + the user is allowed to modify in the metadata." + properties: + annotations: + additionalProperties: + type: string + description: 'Annotations is an unstructured key value map + stored with a resource that may be set by external tools + to store and retrieve arbitrary metadata. They are not queryable + and should be preserved when modifying objects. More info: + http://kubernetes.io/docs/user-guide/annotations' + type: object + generateName: + description: "GenerateName is an optional prefix, used by + the server, to generate a unique name ONLY IF the Name field + has not been provided. If this field is used, the name returned + to the client will be different than the name passed. This + value will also be combined with a unique suffix. The provided + value has the same validation rules as the Name field, and + may be truncated by the length of the suffix required to + make the value unique on the server. \n If this field is + specified and the generated name exists, the server will + NOT return a 409 - instead, it will either return 201 Created + or 500 with Reason ServerTimeout indicating a unique name + could not be found in the time allotted, and the client + should retry (optionally after the time indicated in the + Retry-After header). \n Applied only if Name is not specified. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#idempotency" + type: string + labels: + additionalProperties: + type: string + description: 'Map of string keys and values that can be used + to organize and categorize (scope and select) objects. May + match selectors of replication controllers and services. + More info: http://kubernetes.io/docs/user-guide/labels' + type: object + name: + description: 'Name must be unique within a namespace. Is required + when creating resources, although some resources may allow + a client to request the generation of an appropriate name + automatically. Name is primarily intended for creation idempotence + and configuration definition. Cannot be updated. More info: + http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + namespace: + description: "Namespace defines the space within each name + must be unique. An empty namespace is equivalent to the + \"default\" namespace, but \"default\" is the canonical + representation. Not all objects are required to be scoped + to a namespace - the value of this field for those objects + will be empty. \n Must be a DNS_LABEL. Cannot be updated. + More info: http://kubernetes.io/docs/user-guide/namespaces" + type: string + ownerReferences: + description: List of objects depended by this object. If ALL + objects in the list have been deleted, this object will + be garbage collected. If this object is managed by a controller, + then an entry in this list will point to this controller, + with the controller field set to true. There cannot be more + than one managing controller. + items: + description: OwnerReference contains enough information + to let you identify an owning object. An owning object + must be in the same namespace as the dependent, or be + cluster-scoped, so there is no namespace field. + properties: + apiVersion: + description: API version of the referent. + type: string + blockOwnerDeletion: + description: If true, AND if the owner has the "foregroundDeletion" + finalizer, then the owner cannot be deleted from the + key-value store until this reference is removed. Defaults + to false. To set this field, a user needs "delete" + permission of the owner, otherwise 422 (Unprocessable + Entity) will be returned. + type: boolean + controller: + description: If true, this reference points to the managing + controller. + type: boolean + kind: + description: 'Kind of the referent. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names' + type: string + uid: + description: 'UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids' + type: string + required: + - apiVersion + - kind + - name + - uid + type: object + type: array + type: object + required: + - group + - kind + type: object + required: + - aws + - kubernetes + type: object + status: + description: AdoptedResourceStatus defines the observed status of the + AdoptedResource. + properties: + conditions: + description: A collection of `ackv1alpha1.Condition` objects that + describe the various terminal states of the adopted resource CR + and its target custom resource + items: + description: Condition is the common struct used by all CRDs managed + by ACK service controllers to indicate terminal states of the + CR and its backend AWS service API resource + properties: + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type is the type of the Condition + type: string + required: + - status + - type + type: object + type: array + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index ba90cd1..29265d7 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -30,3 +30,9 @@ If release name contains chart name it will be used as a full name. {{- define "service-account.name" -}} {{ default "default" .Values.serviceAccount.name }} {{- end -}} + +{{- define "watch-namespace" -}} +{{- if eq .Values.installScope "namespace" -}} +{{- .Release.Namespace -}} +{{- end -}} +{{- end -}} diff --git a/helm/templates/cluster-role-binding.yaml b/helm/templates/cluster-role-binding.yaml index ff84bc8..19b7606 100644 --- a/helm/templates/cluster-role-binding.yaml +++ b/helm/templates/cluster-role-binding.yaml @@ -1,11 +1,20 @@ apiVersion: rbac.authorization.k8s.io/v1 +{{ if eq .Values.installScope "cluster" }} kind: ClusterRoleBinding metadata: name: {{ include "app.fullname" . }} roleRef: - apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: {{ include "app.name" . }} +{{ else }} +kind: RoleBinding +metadata: + name: {{ include "app.fullname" . }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role +{{ end }} + apiGroup: rbac.authorization.k8s.io + name: ack-s3-controller subjects: - kind: ServiceAccount name: {{ include "service-account.name" . }} diff --git a/helm/templates/cluster-role-controller.yaml b/helm/templates/cluster-role-controller.yaml index a6a343f..5f131c4 100644 --- a/helm/templates/cluster-role-controller.yaml +++ b/helm/templates/cluster-role-controller.yaml @@ -1,10 +1,16 @@ - ---- apiVersion: rbac.authorization.k8s.io/v1 +{{ if eq .Values.installScope "cluster" }} kind: ClusterRole metadata: creationTimestamp: null name: ack-s3-controller +{{ else }} +kind: Role +metadata: + creationTimestamp: null + name: ack-s3-controller + namespace: {{ .Release.Namespace }} +{{ end }} rules: - apiGroups: - "" @@ -42,3 +48,23 @@ rules: - get - patch - update +- apiGroups: + - services.k8s.aws + resources: + - adoptedresources + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - services.k8s.aws + resources: + - adoptedresources/status + verbs: + - get + - patch + - update diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index 1f7e287..5299f73 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -41,10 +41,16 @@ spec: - "$(AWS_ACCOUNT_ID)" - --aws-region - "$(AWS_REGION)" + - --aws-endpoint-url + - "$(AWS_ENDPOINT_URL)" - --enable-development-logging - "$(ACK_ENABLE_DEVELOPMENT_LOGGING)" - --log-level - "$(ACK_LOG_LEVEL)" + - --resource-tags + - "$(ACK_RESOURCE_TAGS)" + - --watch-namespace + - "$(ACK_WATCH_NAMESPACE)" image: {{ .Values.image.repository }}:{{ .Values.image.tag }} name: controller ports: @@ -56,6 +62,18 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + - name: AWS_ACCOUNT_ID + value: {{ .Values.aws.account_id | quote }} - name: AWS_REGION value: {{ .Values.aws.region }} + - name: AWS_ENDPOINT_URL + value: {{ .Values.aws.endpoint_url | quote }} + - name: ACK_WATCH_NAMESPACE + value: {{ include "watch-namespace" . }} + - name: ACK_ENABLE_DEVELOPMENT_LOGGING + value: {{ .Values.log.enable_development_logging | quote }} + - name: ACK_LOG_LEVEL + value: {{ .Values.log.level | quote }} + - name: ACK_RESOURCE_TAGS + value: {{ join "," .Values.resourceTags | quote }} terminationGracePeriodSeconds: 10 diff --git a/helm/templates/metrics-service.yaml b/helm/templates/metrics-service.yaml new file mode 100644 index 0000000..24cf69f --- /dev/null +++ b/helm/templates/metrics-service.yaml @@ -0,0 +1,30 @@ +{{- if .Values.metrics.service.create }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "app.fullname" . }}-metrics + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: {{ include "app.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: Helm + app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} + k8s-app: {{ include "app.name" . }} + helm.sh/chart: {{ include "chart.name-version" . }} + control-plane: controller +spec: + selector: + app.kubernetes.io/name: {{ include "app.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/managed-by: Helm + k8s-app: {{ include "app.name" . }} +{{- range $key, $value := .Values.deployment.labels }} + {{ $key }}: {{ $value | quote }} +{{- end }} + type: {{ .Values.metrics.service.type }} + ports: + - name: metricsport + port: 8080 + targetPort: 8080 + protocol: TCP +{{- end }} \ No newline at end of file diff --git a/helm/values.yaml b/helm/values.yaml index 1b1872e..0ad70db 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -3,8 +3,8 @@ # Declare variables to be passed into your templates. image: - repository: public.ecr.aws/aws-controllers-k8s/controller - tag: s3-v0.0.2 + repository: public.ecr.aws/aws-controllers-k8s/s3-controller + tag: v0.0.2 pullPolicy: IfNotPresent pullSecrets: [] @@ -16,6 +16,15 @@ deployment: labels: {} containerPort: 8080 +metrics: + service: + # Set to true to automatically create a Kubernetes Service resource for the + # Prometheus metrics server endpoint in controller + create: false + # Which Type to use for the Kubernetes Service? + # See: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + type: "ClusterIP" + resources: requests: memory: "64Mi" @@ -27,6 +36,23 @@ resources: aws: # If specified, use the AWS region for AWS API calls region: "" + account_id: "" + endpoint_url: "" + +# log level for the controller +log: + enable_development_logging: false + level: info + +# Set to "namespace" to install the controller in a namespaced scope, will only watch for object creation +# in the namespace. By default installScope is cluster wide. +installScope: cluster + +resourceTags: + # Configures the ACK service controller to always set key/value pairs tags on resources that it manages. + - services.k8s.aws/managed=true + - services.k8s.aws/created=%UTCNOW% + - services.k8s.aws/namespace=%KUBERNETES_NAMESPACE% serviceAccount: # Specifies whether a service account should be created diff --git a/pkg/resource/bucket/delta.go b/pkg/resource/bucket/delta.go index 48ed6f3..2d9824b 100644 --- a/pkg/resource/bucket/delta.go +++ b/pkg/resource/bucket/delta.go @@ -148,6 +148,13 @@ func newResourceDelta( delta.Add("Spec.OwnershipControls", a.ko.Spec.OwnershipControls, b.ko.Spec.OwnershipControls) } else if a.ko.Spec.OwnershipControls != nil && b.ko.Spec.OwnershipControls != nil { + } + if ackcompare.HasNilDifference(a.ko.Spec.Policy, b.ko.Spec.Policy) { + delta.Add("Spec.Policy", a.ko.Spec.Policy, b.ko.Spec.Policy) + } else if a.ko.Spec.Policy != nil && b.ko.Spec.Policy != nil { + if *a.ko.Spec.Policy != *b.ko.Spec.Policy { + delta.Add("Spec.Policy", a.ko.Spec.Policy, b.ko.Spec.Policy) + } } if ackcompare.HasNilDifference(a.ko.Spec.RequestPayment, b.ko.Spec.RequestPayment) { delta.Add("Spec.RequestPayment", a.ko.Spec.RequestPayment, b.ko.Spec.RequestPayment) diff --git a/pkg/resource/bucket/hook.go b/pkg/resource/bucket/hook.go index f7f7def..c6a491f 100644 --- a/pkg/resource/bucket/hook.go +++ b/pkg/resource/bucket/hook.go @@ -28,6 +28,7 @@ var ( DefaultAccelerationConfigurationStatus = svcsdk.BucketAccelerateStatusSuspended DefaultRequestPayer = svcsdk.PayerBucketOwner DefaultVersioningStatus = svcsdk.BucketVersioningStatusSuspended + DefaultPolicy = "" ) var ( @@ -63,6 +64,11 @@ func (rm *resourceManager) createPutFields( return err } } + if r.ko.Spec.Policy != nil { + if err := rm.syncPolicy(ctx, r); err != nil { + return err + } + } if r.ko.Spec.RequestPayment != nil { if err := rm.syncRequestPayment(ctx, r); err != nil { return err @@ -139,6 +145,11 @@ func (rm *resourceManager) customUpdateBucket( return nil, err } } + if delta.DifferentAt("Spec.Policy") { + if err := rm.syncPolicy(ctx, desired); err != nil { + return nil, err + } + } if delta.DifferentAt("Spec.RequestPayment") { if err := rm.syncRequestPayment(ctx, desired); err != nil { return nil, err @@ -222,6 +233,16 @@ func (rm *resourceManager) addPutFieldsToSpec( } ko.Spec.OwnershipControls = rm.setResourceOwnershipControls(r, getOwnershipControlsResponse) + getPolicyResponse, err := rm.sdkapi.GetBucketPolicyWithContext(ctx, rm.newGetBucketPolicyPayload(r)) + if err != nil { + if awsErr, ok := ackerr.AWSError(err); ok && awsErr.Code() == "NoSuchBucketPolicy" { + getPolicyResponse = &svcsdk.GetBucketPolicyOutput{} + } else { + return err + } + } + ko.Spec.Policy = getPolicyResponse.Policy + getRequestPaymentResponse, err := rm.sdkapi.GetBucketRequestPaymentWithContext(ctx, rm.newGetBucketRequestPaymentPayload(r)) if err != nil { return nil @@ -597,6 +618,82 @@ func (rm *resourceManager) syncOwnershipControls( return nil } +func (rm *resourceManager) newGetBucketPolicyPayload( + r *resource, +) *svcsdk.GetBucketPolicyInput { + res := &svcsdk.GetBucketPolicyInput{} + res.SetBucket(*r.ko.Spec.Name) + return res +} + +func (rm *resourceManager) newPutBucketPolicyPayload( + r *resource, +) *svcsdk.PutBucketPolicyInput { + res := &svcsdk.PutBucketPolicyInput{} + res.SetBucket(*r.ko.Spec.Name) + res.SetConfirmRemoveSelfBucketAccess(false) + if r.ko.Spec.Policy != nil { + res.SetPolicy(*r.ko.Spec.Policy) + } else { + res.SetPolicy(DefaultPolicy) + } + return res +} + +func (rm *resourceManager) newDeleteBucketPolicyPayload( + r *resource, +) *svcsdk.DeleteBucketPolicyInput { + res := &svcsdk.DeleteBucketPolicyInput{} + res.SetBucket(*r.ko.Spec.Name) + return res +} + +func (rm *resourceManager) putPolicy( + ctx context.Context, + r *resource, +) (err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.putPolicy") + defer exit(err) + input := rm.newPutBucketPolicyPayload(r) + + _, err = rm.sdkapi.PutBucketPolicyWithContext(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "PutBucketPolicy", err) + if err != nil { + return err + } + + return nil +} + +func (rm *resourceManager) deletePolicy( + ctx context.Context, + r *resource, +) (err error) { + rlog := ackrtlog.FromContext(ctx) + exit := rlog.Trace("rm.deletePolicy") + defer exit(err) + input := rm.newDeleteBucketPolicyPayload(r) + + _, err = rm.sdkapi.DeleteBucketPolicyWithContext(ctx, input) + rm.metrics.RecordAPICall("UPDATE", "DeleteBucketPolicy", err) + if err != nil { + return err + } + + return nil +} + +func (rm *resourceManager) syncPolicy( + ctx context.Context, + r *resource, +) (err error) { + if r.ko.Spec.Policy == nil { + return rm.deletePolicy(ctx, r) + } + return rm.putPolicy(ctx, r) +} + func (rm *resourceManager) newGetBucketRequestPaymentPayload( r *resource, ) *svcsdk.GetBucketRequestPaymentInput { diff --git a/test/e2e/resources/bucket_policy.yaml b/test/e2e/resources/bucket_policy.yaml new file mode 100644 index 0000000..fb4521a --- /dev/null +++ b/test/e2e/resources/bucket_policy.yaml @@ -0,0 +1,19 @@ +apiVersion: s3.services.k8s.aws/v1alpha1 +kind: Bucket +metadata: + name: $BUCKET_NAME +spec: + name: $BUCKET_NAME + policy: > + { + "Version": "2012-10-17", + "Id": "BlockAllObjects", + "Statement": [ + { + "Effect": "Deny", + "Principal": "*", + "Action": "s3:PutObject", + "Resource": "arn:aws:s3:::$BUCKET_NAME/*" + } + ] + } \ No newline at end of file diff --git a/test/e2e/tests/test_bucket.py b/test/e2e/tests/test_bucket.py index ea9a100..83add51 100644 --- a/test/e2e/tests/test_bucket.py +++ b/test/e2e/tests/test_bucket.py @@ -18,6 +18,7 @@ import pytest import time import logging +import re from acktest.resources import random_suffix_name from acktest.k8s import resource as k8s @@ -169,6 +170,20 @@ def test_ownership_controls(self, s3_client): delete_bucket(s3_client, ref, resource_name) + def test_policy(self, s3_client, s3_resource): + (ref, resource_name, resource_data) = create_bucket(s3_client, "bucket_policy") + + bucket = get_bucket(s3_resource, resource_name) + policy = bucket.Policy() + + # Strip any whitespace from between the two + desired = re.sub(r"\s+", "", resource_data["spec"]["policy"], flags=re.UNICODE) + latest = re.sub(r"\s+", "", policy.policy, flags=re.UNICODE) + + assert desired == latest + + delete_bucket(s3_client, ref, resource_name) + def test_request_payment(self, s3_client, s3_resource): (ref, resource_name, resource_data) = create_bucket(s3_client, "bucket_request_payment")