diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..14d61f9 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,76 @@ +name: CI + +on: + push: + branches: + - main + - release-* + workflow_dispatch: {} + +env: + DOCKER_BUILDX_VERSION: 'v0.8.2' + + XPKG_ACCESS_ID: ${{ secrets.XPKG_ACCESS_ID }} + +jobs: + detect-noop: + runs-on: ubuntu-22.04 + outputs: + noop: ${{ steps.noop.outputs.should_skip }} + steps: + - name: Detect No-op Changes + id: noop + uses: fkirc/skip-duplicate-actions@v2.0.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + paths_ignore: '["**.md", "**.png", "**.jpg"]' + do_not_skip: '["workflow_dispatch", "schedule", "push"]' + + publish-artifacts: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Setup QEMU + uses: docker/setup-qemu-action@v1 + with: + platforms: all + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + version: ${{ env.DOCKER_BUILDX_VERSION }} + install: true + + - name: Checkout + uses: actions/checkout@v2 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Build Artifacts + run: make -j2 build.all + env: + # We're using docker buildx, which doesn't actually load the images it + # builds by default. Specifying --load does so. + BUILD_ARGS: "--load" + + - name: Publish Artifacts to GitHub + uses: actions/upload-artifact@v2 + with: + name: output + path: _output/** + + - name: Login to Upbound + uses: docker/login-action@v1 + if: env.XPKG_ACCESS_ID != '' + with: + registry: xpkg.upbound.io + username: ${{ secrets.XPKG_ACCESS_ID }} + password: ${{ secrets.XPKG_TOKEN }} + + - name: Publish Artifacts + run: make -j2 publish BRANCH_NAME=${GITHUB_REF##*/} diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 0000000..4c44a66 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,13 @@ +name: End to End Testing + +on: + issue_comment: + types: [created] + +jobs: + e2e: + uses: upbound/uptest/.github/workflows/pr-comment-trigger.yml@main + with: + package-type: configuration + secrets: + UPTEST_CLOUD_CREDENTIALS: ${{ secrets.UPTEST_CLOUD_CREDENTIALS }} diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 0000000..96a8af7 --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,26 @@ +name: Tag + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g. v0.1.0)' + required: true + message: + description: 'Tag message' + required: true + +jobs: + create-tag: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Create Tag + uses: negz/create-tag@v1 + with: + version: ${{ github.event.inputs.version }} + message: ${{ github.event.inputs.message }} + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ae43290 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +/.cache +/.work +/_output +/results + +*.xpkg +kubeconfig +.DS_Store \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c2fad47 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "build"] + path = build + url = https://github.com/upbound/build diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0ef025d --- /dev/null +++ b/Makefile @@ -0,0 +1,81 @@ +# Project Setup +PROJECT_NAME := platform-ref-s3-website +PROJECT_REPO := github.com/upbound/$(PROJECT_NAME) + +# NOTE(hasheddan): the platform is insignificant here as Configuration package +# images are not architecture-specific. We constrain to one platform to avoid +# needlessly pushing a multi-arch image. +PLATFORMS ?= linux_amd64 +-include build/makelib/common.mk + +# ==================================================================================== +# Setup Kubernetes tools + +UP_VERSION = v0.18.0 +UP_CHANNEL = stable +UPTEST_VERSION = v0.5.0 +UPTEST_CLAIMS = examples/website.yaml,examples/content.yaml +# ==================================================================================== +# Setup Helm + +USE_HELM3 = true +HELM3_VERSION = v3.11.2 +HELM_OCI_URL = xpkg.upbound.io/upbound +HELM_CHARTS = $(PROJECT_NAME)-chart +HELM_CHART_LINT_ARGS_$(PROJECT_NAME) = --set nameOverride='',imagePullSecrets='' +HELM_CHARTS_DIR ?= $(ROOT_DIR)/chart + +-include build/makelib/k8s_tools.mk + +# ==================================================================================== +# Setup XPKG +XPKG_REG_ORGS ?= xpkg.upbound.io/upbound +# NOTE(hasheddan): skip promoting on xpkg.upbound.io as channel tags are +# inferred. +XPKG_REG_ORGS_NO_PROMOTE ?= xpkg.upbound.io/upbound +XPKGS = $(PROJECT_NAME) +XPKG_DIR := $(ROOT_DIR)/apis +-include build/makelib/xpkg.mk + +CROSSPLANE_NAMESPACE = upbound-system +-include build/makelib/local.xpkg.mk +-include build/makelib/controlplane.mk + +# ==================================================================================== +# Targets + +# run `make help` to see the targets and options + +# We want submodules to be set up the first time `make` is run. +# We manage the build/ folder and its Makefiles as a submodule. +# The first time `make` is run, the includes of build/*.mk files will +# all fail, and this target will be run. The next time, the default as defined +# by the includes will be run instead. +fallthrough: submodules + @echo Initial setup complete. Running make again . . . + @make + +# Update the submodules, such as the common build scripts. +submodules: + @git submodule sync + @git submodule update --init --recursive + +# We must ensure up is installed in tool cache prior to build as including the k8s_tools machinery prior to the xpkg +# machinery sets UP to point to tool cache. +build.init: $(UP) + +# ==================================================================================== +# End to End Testing + +# This target requires the following environment variables to be set: +# - UPTEST_CLOUD_CREDENTIALS, cloud credentials for the provider being tested, e.g. export UPTEST_CLOUD_CREDENTIALS=$(cat ~/.aws/credentials) +uptest: $(UPTEST) $(KUBECTL) $(KUTTL) + @$(INFO) running automated tests + @KUBECTL=$(KUBECTL) KUTTL=$(KUTTL) $(UPTEST) e2e $(UPTEST_CLAIMS) --setup-script=test/setup.sh --default-timeout=2400 || $(FAIL) + @$(OK) running automated tests + +# This target requires the following environment variables to be set: +# - UPTEST_CLOUD_CREDENTIALS, cloud credentials for the provider being tested, e.g. export UPTEST_CLOUD_CREDENTIALS=$(cat ~/.aws/credentials) +e2e: build controlplane.up local.xpkg.deploy.configuration.$(PROJECT_NAME) uptest + +.PHONY: uptest e2e diff --git a/README.md b/README.md index 87bc643..0a1ce5a 100644 --- a/README.md +++ b/README.md @@ -1 +1,273 @@ # platform-ref-s3-website + +This repository defines a [Crossplane configuration package](https://docs.crossplane.io/latest/concepts/packages/#configuration-packages) that demonstrates provisioning and hosting static s3 websites. + +## Composition Overview + +The Amazon S3 static website hosting reference platform configuration serves as a foundation for building, running, and managing a static website using Amazon S3. A static website primarily consists of webpages that include fixed content and may incorporate client-side scripts. + +```mermaid +flowchart LR + +subgraph "XWebsite" + CloudFront_Distribution["CloudFront \n Distribution"] + CloudFront_Origin["CloudFront \n OriginAccessIdentity"] + ACM_Certificate["ACM \n Certificate"] + Route53_Record["Route53 \n A-Record"] + Route53_Record2["Route53 \n CNAME"] + S3_Bucket["S3 \n Bucket"] + + CloudFront_Distribution -->|Restricts Access| CloudFront_Origin + CloudFront_Distribution --> Route53_Record + ACM_Certificate --> Route53_Record2 + ACM_Certificate --> CloudFront_Distribution + CloudFront_Distribution -->|Serves| S3_Bucket +end + +subgraph "XContent" + Object["S3 \n Object"] + Object --> |Uploads| S3_Bucket +end + +subgraph "XDNSZone" + Zone["Route53 \n Zone"] + Route53_Record --> Zone + Route53_Record2 --> Zone +end +``` + +- [XWebsite](apis/XWebsite/): creates s3,cdn,acm,route53 configuration for static website hosting + - [XContent](apis/XContent/): creates s3 object for static website hosting + - [XDNSZone](apis/XDNSZone/): creates route53 public DNS Zone (use only if needed) + +## Deploying the Reference Platform + +> **Note:** Before proceeding with the setup of the Amazon S3 static website hosting reference platform, please ensure that you have a working Route 53 Zone associated with your AWS Account. The Route 53 zone is essential to manage DNS records and route traffic to your website. +> If you already have a working Route 53 Zone with the domain you intend to use, you can proceed with the configuration of the static S3 website hosting. However, if you do not have a Route 53 zone set up for your domain, you must first create one. You may want to follow an example `example/zone.yaml` to ensure your zone is working correctly. +> Once the Route 53 zone is in place, you can continue with the Amazon S3 static website hosting reference platform. + + +First you will need access to a Kubernetes cluster. Ensure you are +using the correct context: + +```sh +kubectl config current-context +``` + +Next, we'll use the `up` binary to install UXP, Upbound's distribution of +Crossplane. To get `up`, follow the [installation instructions](https://docs.upbound.io/uxp). + +To install UXP using `up` run: + +```console +up uxp install +UXP 1.12.1-up.1 installed +``` + +Install the AWS Provider: + +```console +kubectl apply -f examples/provider-aws-scoped.yaml + +provider.pkg.crossplane.io/provider-family-aws created +provider.pkg.crossplane.io/provider-aws-acm created +provider.pkg.crossplane.io/provider-aws-cloudfront created +provider.pkg.crossplane.io/provider-aws-route53 configured +provider.pkg.crossplane.io/provider-aws-s3 created +``` + +You can keep track of the provider install: + +```console +kubectl get -f examples/provider-aws-scoped.yaml +``` + +All the providers should be `INSTALLED` and `HEALTHY` within a minute or two: + +```console +providerrevision.pkg.crossplane.io/provider-aws-acm-cd915da2bc0f True 1 xpkg.upbound.io/upbound/provider-aws-acm:v0.37.0 Active 1 1 113m +providerrevision.pkg.crossplane.io/provider-aws-cloudfront-9da29e4a8982 True 1 xpkg.upbound.io/upbound/provider-aws-cloudfront:v0.37.0 Active 1 1 113m +providerrevision.pkg.crossplane.io/provider-aws-route53-27d581b1b384 True 1 xpkg.upbound.io/upbound/provider-aws-route53:v0.37.0 Active 1 1 113m +providerrevision.pkg.crossplane.io/provider-aws-s3-dbc7f981d81f True 1 xpkg.upbound.io/upbound/provider-aws-s3:v0.37.0 Active 1 1 113m +providerrevision.pkg.crossplane.io/provider-family-aws-d095ac1e13dd True 1 xpkg.upbound.io/upbound/provider-family-aws:v0.37.0 Active 7d4h +``` + +Next, install the CompositeResourceDefinitions and Compositions: + +```console +kubectl apply -f apis/XContent +kubectl apply -f apis/XWebsite +``` + +The Custom Platform APIs are Kubernetes `CompositeResourceDefinition` objects or `XRD` +for short. We can list them using `kubectl`: + +```console +kubectl get xrd +``` + +The following XRDs should be `ESTABLISHED` and `OFFERED`: + +```console +NAME ESTABLISHED OFFERED AGE +xwebsites.example.upbound.io True True 109m +xcontents.example.upbound.io True True 109m +``` + +## Authenticating to AWS + +Now that Crossplane, the Provider and all the Compositions are installed we +need to give the provider AWS credentials. This is done by creating a `ProviderConfig`. + +There are many options we can use to authenticate to AWS, but to sim + +```sh +kubectl create secret generic aws-creds -n upbound-system --from-file=creds=./creds.conf +``` + +## Authenticating with AWS + +Once Crossplane, the Provider, and all the Compositions are installed, the next step is to provide AWS credentials to the Provider. This is achieved by creating a ProviderConfig. + +For simplicity, we will use the following method to authenticate with AWS: + +```sh +kubectl create secret generic aws-creds -n upbound-system --from-file=creds=./creds.conf +``` + +### Configuring the Provider with AWS Credentials + +To configure the Provider with the AWS credentials obtained in the previous step, create the following `ProviderConfig` object. For more authentication options, such as IRSA, refer to [AUTHENTICATION](https://github.com/upbound/provider-aws/blob/main/AUTHENTICATION.md). + +```yaml +apiVersion: aws.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default +spec: + credentials: + source: Secret + secretRef: + namespace: upbound-system + name: aws-creds + key: creds +``` + +Apply the configuration using the command: + +```console +kubectl apply -f examples/providerconfig-creds.yaml +``` + +Now, the examples are ready to be deployed. + +Using files in the `examples` directory: + +```console +kubectl apply -f examples/ns.yaml +kubectl apply -f examples/website.yaml +kubectl apply -f examples/content.yaml +``` + +Applying the examples to the cluster would create Kubernetes objects similar +to the following: + +```console +kubectl get websites +``` + +```console +NAME SYNCED READY COMPOSITION AGE +xcontent.example.upbound.io/demo-9fsxm True True xcontents.example.upbound.io 4m21s + +NAME DOMAIN ENABLED SYNCED READY COMPOSITION AGE +xwebsite.example.upbound.io/website-vdc75 demo.website.upbound.io true True True xwebsites.example.upbound.io 37m +``` + +## Cleaning Up + +To Clean up the installation, run the following commands: + +```console +kubectl delete -f examples/content.yaml +kubectl delete -f examples/website.yaml +``` + +Wait for all the cloud resources to be deleted: + +```console +kubectl get managed +``` + +Delete the Compositions, Providers, and ProviderConfig after all the resources have been deleted. + +```console +kubectl delete -f examples/ns.yaml +kubectl delete -f apis/XWebsite +kubectl delete -f apis/XContent +kubectl delete -f examples/providerconfig-creds.yaml +kubectl delete -f examples/provider-aws-scoped.yaml +``` + +## Local Development + +This reference platform is a starting point to help you build your own +Platform APIs. + +The following sections will detail how to make, test, and publish +modifications to these compositions. + +### Setting Up the Build Environment + +Clone this repository: + +```console +git clone https://github.com/upbound/platform-ref-s3-website +``` + +Next pull in the Upbound [build](https://github.com/upbound/build) as a git submodule: + +```console +cd platform-ref-s3-website +make submodules +Submodule 'build' (https://github.com/upbound/build) registered for path 'build' +Cloning into '/home/user/platform-ref-s3-website/build'... +Submodule path 'build': checked out '292f958d2d97f26b450723998f82f7fc1767920c' +``` + +Next run `make`. This will download the required components: + +```sh +make +``` + +### Applying your Updated Compositions to a Cluster + +### Automated Testing Using Uptest + +[Uptest](https://github.com/upbound/uptest) is used for end to end testing of the +Compositions in this repository. It does this by provisioning example claims and +waiting for them to become `READY`. + +To run uptest locally, first set the `UPTEST_CLOUD_CREDENTIALS` environment variable +with the contents of an AWS credentials file: + +```sh +export UPTEST_CLOUD_CREDENTIALS=$(cat ~/.aws/credentials) +``` + +With the credentials file of the following format: + +```ini +[default] +aws_access_key_id=AKIA... +aws_secret_access_key=jQplCPbh... +``` + +```sh +make e2e +``` + +## Questions? + +For any questions, thoughts and comments don't hesitate to reach out or drop by slack.crossplane.io, and say hi! diff --git a/apis/XContent/composition.yaml b/apis/XContent/composition.yaml new file mode 100644 index 0000000..d5f5109 --- /dev/null +++ b/apis/XContent/composition.yaml @@ -0,0 +1,68 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + labels: + crossplane.io/provider: aws + name: xcontents.example.upbound.io +spec: + compositeTypeRef: + apiVersion: example.upbound.io/v1alpha1 + kind: XContent + patchSets: + - name: providerConfigRef + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.providerConfigRef.name + toFieldPath: spec.providerConfigRef.name + - name: region + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.region + toFieldPath: spec.forProvider.region + - name: deletionPolicy + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.deletionPolicy + toFieldPath: spec.deletionPolicy + - name: userTags + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.tags + toFieldPath: spec.forProvider.tags + policy: + mergeOptions: + appendSlice: true + keepMapValues: false + + resources: + - name: Object + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: BucketObject + metadata: + labels: + resource: BucketObject + spec: + forProvider: + contentType: text/html + patches: + - patchSetName: providerConfigRef + type: PatchSet + - patchSetName: region + type: PatchSet + - patchSetName: deletionPolicy + type: PatchSet + - patchSetName: userTags + type: PatchSet + - fromFieldPath: spec.parameters.bucket.name + toFieldPath: spec.forProvider.bucketRef.name + - fromFieldPath: spec.parameters.content + toFieldPath: spec.forProvider.content + - combine: + strategy: string + string: + fmt: '%s/index.html' + variables: + - fromFieldPath: spec.parameters.bucket.path + toFieldPath: spec.forProvider.key + type: CombineFromComposite diff --git a/apis/XContent/definition.yaml b/apis/XContent/definition.yaml new file mode 100644 index 0000000..16c5164 --- /dev/null +++ b/apis/XContent/definition.yaml @@ -0,0 +1,75 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xcontents.example.upbound.io +spec: + claimNames: + kind: Content + plural: contents + group: example.upbound.io + names: + categories: + - websites + - composition + - crossplane + kind: XContent + plural: xcontents + versions: + - name: v1alpha1 + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + description: s3 object content configuration parameters. + properties: + region: + type: string + description: Region is the region youre bucket is located. + bucket: + description: "Bucket Name and Path to place content" + properties: + name: + description: The name of the bucket to add static content. + type: string + path: + description: The path in the Bucket to add content. + type: string + type: object + content: + description: Literal string value to use as the object content, which will be uploaded as UTF-8-encoded text. + type: string + tags: + description: Map of additional AWS Tags to add to resources + additionalProperties: + type: string + type: object + deletionPolicy: + type: string + description: DeletionPolicy specifies what will happen to the underlying external when this managed resource is deleted - either "Delete" or "Orphan" the external resource. + providerConfigRef: + type: object + description: crossplane provider credentials to use. + properties: + name: + type: string + required: + - name + required: + - parameters + - deletionPolicy + - providerConfigRef + status: + properties: + content: + description: Useful values generated from the S3 API. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + type: object diff --git a/apis/XDNSZone/composition.yaml b/apis/XDNSZone/composition.yaml new file mode 100644 index 0000000..c9aad17 --- /dev/null +++ b/apis/XDNSZone/composition.yaml @@ -0,0 +1,58 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + labels: + crossplane.io/provider: aws + name: xdnszones.example.upbound.io +spec: + compositeTypeRef: + apiVersion: example.upbound.io/v1alpha1 + kind: XDNSZone + patchSets: + - name: providerConfigRef + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.providerConfigRef.name + toFieldPath: spec.providerConfigRef.name + - name: region + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.region + toFieldPath: spec.forProvider.region + - name: deletionPolicy + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.deletionPolicy + toFieldPath: spec.deletionPolicy + - name: userTags + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.tags + toFieldPath: spec.forProvider.tags + policy: + mergeOptions: + appendSlice: true + keepMapValues: false + + resources: + - name: Zone + base: + apiVersion: route53.aws.upbound.io/v1beta1 + kind: Zone + metadata: + labels: + resource: Zone + patches: + - patchSetName: providerConfigRef + type: PatchSet + - patchSetName: region + type: PatchSet + - patchSetName: deletionPolicy + type: PatchSet + - patchSetName: userTags + type: PatchSet + - fromFieldPath: spec.parameters.domain.zoneName + toFieldPath: spec.forProvider.name + - fromFieldPath: status.atProvider.zoneId + toFieldPath: status.hostedzone.zoneId + type: ToCompositeFieldPath diff --git a/apis/XDNSZone/definition.yaml b/apis/XDNSZone/definition.yaml new file mode 100644 index 0000000..08d6eef --- /dev/null +++ b/apis/XDNSZone/definition.yaml @@ -0,0 +1,74 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xdnszones.example.upbound.io +spec: + claimNames: + kind: DNSZone + plural: dnszones + group: example.upbound.io + names: + categories: + - websites + - composition + - crossplane + kind: XDNSZone + plural: xdnszones + versions: + - name: v1alpha1 + additionalPrinterColumns: + - description: The zoneId from route53 Zone + jsonPath: .status.hostedzone.zoneId + name: zoneId + type: string + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + description: dns zone configuration parameters. + properties: + region: + type: string + description: Region is the region youre bucket is located. + domain: + description: domain configuration field + properties: + zoneName: + description: Name of the hosted zone. + type: string + type: object + tags: + description: Map of additional AWS Tags to add to resources + additionalProperties: + type: string + type: object + deletionPolicy: + type: string + description: DeletionPolicy specifies what will happen to the underlying external when this managed resource is deleted - either "Delete" or "Orphan" the external resource. + providerConfigRef: + type: object + description: crossplane provider credentials to use. + properties: + name: + type: string + required: + - name + required: + - parameters + - deletionPolicy + - providerConfigRef + status: + properties: + hostedzone: + description: Useful values generated from the S3 API. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + type: object diff --git a/apis/XWebsite/composition.yaml b/apis/XWebsite/composition.yaml new file mode 100644 index 0000000..d2727fd --- /dev/null +++ b/apis/XWebsite/composition.yaml @@ -0,0 +1,384 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: Composition +metadata: + labels: + crossplane.io/provider: aws + name: xwebsites.example.upbound.io +spec: + compositeTypeRef: + apiVersion: example.upbound.io/v1alpha1 + kind: XWebsite + patchSets: + - name: providerConfigRef + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.providerConfigRef.name + toFieldPath: spec.providerConfigRef.name + - name: region + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.region + toFieldPath: spec.forProvider.region + - name: deletionPolicy + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.deletionPolicy + toFieldPath: spec.deletionPolicy + - name: userTags + patches: + - type: FromCompositeFieldPath + fromFieldPath: spec.parameters.tags + toFieldPath: spec.forProvider.tags + policy: + mergeOptions: + appendSlice: true + keepMapValues: false + + resources: + - name: cdn-distribution + base: + apiVersion: cloudfront.aws.upbound.io/v1beta1 + kind: Distribution + metadata: + labels: + resource: Distribution + spec: + forProvider: + customErrorResponses: + - errorCachingMinTtl: 10 + errorCode: 403 + responseCode: "200" + responsePagePath: / + - errorCachingMinTtl: 10 + errorCode: 404 + responseCode: "200" + responsePagePath: / + defaultCacheBehavior: + - allowedMethods: + - HEAD + - GET + - OPTIONS + cachedMethods: + - HEAD + - GET + cachePolicyId: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad # cache-disabled + targetOriginId: s3Origin + viewerProtocolPolicy: redirect-to-https + defaultRootObject: index.html + enabled: true + origin: + - originId: s3Origin + viewerCertificate: + - cloudFrontDefaultCertificate: false + minimumProtocolVersion: TLSv1.2_2021 + sslSupportMethod: sni-only + restrictions: + - geoRestriction: + - restrictionType: none + patches: + - patchSetName: providerConfigRef + type: PatchSet + - patchSetName: region + type: PatchSet + - patchSetName: deletionPolicy + type: PatchSet + - patchSetName: userTags + type: PatchSet + - fromFieldPath: spec.parameters.enabled + toFieldPath: spec.forProvider.enabled + - fromFieldPath: spec.parameters.defaultRootObject + toFieldPath: spec.forProvider.defaultRootObject + - fromFieldPath: spec.parameters.description + toFieldPath: spec.forProvider.comment + - combine: + strategy: string + string: + fmt: '%s.s3.%s.amazonaws.com' + variables: + - fromFieldPath: spec.parameters.bucket.name + - fromFieldPath: spec.parameters.region + toFieldPath: spec.forProvider.origin[0].domainName + type: CombineFromComposite + - fromFieldPath: spec.parameters.bucket.path + toFieldPath: spec.forProvider.origin[0].originPath + - fromFieldPath: status.atProvider.domainName + toFieldPath: status.cloudfront.domain + type: ToCompositeFieldPath + - fromFieldPath: status.atProvider.arn + toFieldPath: status.cloudfront.arn + type: ToCompositeFieldPath + - fromFieldPath: status.cloudfront.oai + policy: + fromFieldPath: Required + toFieldPath: spec.forProvider.origin[0].s3OriginConfig[0].originAccessIdentity + transforms: + - string: + fmt: origin-access-identity/cloudfront/%s + type: string + - fromFieldPath: spec.parameters.domain.name + toFieldPath: spec.forProvider.aliases[0] + - fromFieldPath: status.certificate.arn + toFieldPath: spec.forProvider.viewerCertificate[0].acmCertificateArn + + - name: cdn-oai + base: + apiVersion: cloudfront.aws.upbound.io/v1beta1 + kind: OriginAccessIdentity + metadata: + labels: + resource: OriginAccessIdentity + patches: + - patchSetName: providerConfigRef + type: PatchSet + - patchSetName: region + type: PatchSet + - patchSetName: deletionPolicy + type: PatchSet + - fromFieldPath: metadata.name + toFieldPath: spec.forProvider.comment + - fromFieldPath: status.atProvider.id + toFieldPath: status.cloudfront.oai + type: ToCompositeFieldPath + + - name: s3-bucket + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: Bucket + metadata: + labels: + resource: Bucket + spec: + forProvider: + acl: private + publicAccessBlockConfiguration: + blockPublicAcls: true + blockPublicPolicy: true + ignorePublicAcls: true + restrictPublicBuckets: true + serverSideEncryptionConfiguration: + rules: + - applyServerSideEncryptionByDefault: + sseAlgorithm: AES256 + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + - type: PatchSet + patchSetName: userTags + - fromFieldPath: spec.parameters.bucket.forceDestroy + toFieldPath: spec.forProvider.forceDestroy + - fromFieldPath: spec.parameters.bucket.name + toFieldPath: metadata.name + - type: ToCompositeFieldPath + fromFieldPath: metadata.annotations[crossplane.io/external-name] + toFieldPath: status.s3.name + - type: ToCompositeFieldPath + fromFieldPath: status.atProvider.arn + toFieldPath: status.s3.arn + + - name: s3-blockpublicaccess + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: BucketPublicAccessBlock + metadata: + labels: + resource: BucketPublicAccessBlock + spec: + forProvider: + lockPublicAcls: true + blockPublicPolicy: true + ignorePublicAcls: true + restrictPublicBuckets: true + bucketSelector: + matchControllerRef: true + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + - type: PatchSet + patchSetName: userTags + + - name: s3-serversideencryption + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: BucketServerSideEncryptionConfiguration + metadata: + labels: + resource: BucketServerSideEncryptionConfiguration + spec: + forProvider: + bucketSelector: + matchControllerRef: true + rule: + - applyServerSideEncryptionByDefault: + - sseAlgorithm: AES256 + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + - type: PatchSet + patchSetName: userTags + + - name: s3-bucketVersioning + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: BucketVersioning + metadata: + labels: + resource: BucketVersioning + spec: + forProvider: + bucketSelector: + matchControllerRef: true + versioningConfiguration: + - status: Enabled + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + + - name: s3-bucket-policy + base: + apiVersion: s3.aws.upbound.io/v1beta1 + kind: BucketPolicy + metadata: + labels: + resource: BucketPolicy + spec: + forProvider: + bucketSelector: + matchControllerRef: true + patches: + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + - combine: + strategy: string + string: + fmt: |- + { + "Version": "2008-10-17", + "Id": "PolicyForCloudFrontPrivateContent", + "Statement": [ + { + "Sid": "AllowAccessForOAI", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity %s" + }, + "Action": "s3:GetObject", + "Resource": "arn:aws:s3:::%s/*" + } + ] + } + variables: + - fromFieldPath: status.cloudfront.oai + - fromFieldPath: spec.parameters.bucket.name + policy: + fromFieldPath: Required + toFieldPath: spec.forProvider.policy + type: CombineFromComposite + + - name: acm-certificate-us + base: + apiVersion: acm.aws.upbound.io/v1beta1 + kind: Certificate + metadata: + labels: + resource: Certificate + spec: + forProvider: + region: us-east-1 + validationMethod: DNS + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: deletionPolicy + - fromFieldPath: spec.parameters.domain.name + toFieldPath: spec.forProvider.domainName + - fromFieldPath: status.atProvider.arn + toFieldPath: status.certificate.arn + type: ToCompositeFieldPath + - fromFieldPath: status.atProvider.domainValidationOptions + toFieldPath: status.certificate.domainValidationOptions + type: ToCompositeFieldPath + + - name: a-record-cdn + base: + apiVersion: route53.aws.upbound.io/v1beta1 + kind: Record + metadata: + labels: + resource: Record + type: A + spec: + forProvider: + alias: + - evaluateTargetHealth: false + zoneId: Z2FDTNDATAQYW2 + type: A + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + - fromFieldPath: spec.parameters.domain.name + toFieldPath: spec.forProvider.name + - fromFieldPath: spec.parameters.domain.zoneId + toFieldPath: spec.forProvider.zoneId + - fromFieldPath: status.cloudfront.domain + policy: + fromFieldPath: Required + toFieldPath: spec.forProvider.alias[0].name + + - name: cname-validation-record + base: + apiVersion: route53.aws.upbound.io/v1beta1 + kind: Record + metadata: + labels: + resource: Record + type: CNAME + spec: + forProvider: + ttl: 300 + patches: + - type: PatchSet + patchSetName: providerConfigRef + - type: PatchSet + patchSetName: region + - type: PatchSet + patchSetName: deletionPolicy + - fromFieldPath: spec.parameters.domain.zoneId + toFieldPath: spec.forProvider.zoneId + - fromFieldPath: status.certificate.domainValidationOptions[0].resourceRecordName + policy: + fromFieldPath: Required + toFieldPath: spec.forProvider.name + - fromFieldPath: status.certificate.domainValidationOptions[0].resourceRecordValue + policy: + fromFieldPath: Required + toFieldPath: spec.forProvider.records[0] + - fromFieldPath: status.certificate.domainValidationOptions[0].resourceRecordType + policy: + fromFieldPath: Required + toFieldPath: spec.forProvider.type diff --git a/apis/XWebsite/definition.yaml b/apis/XWebsite/definition.yaml new file mode 100644 index 0000000..cf20aeb --- /dev/null +++ b/apis/XWebsite/definition.yaml @@ -0,0 +1,113 @@ +apiVersion: apiextensions.crossplane.io/v1 +kind: CompositeResourceDefinition +metadata: + name: xwebsites.example.upbound.io +spec: + claimNames: + kind: Website + plural: websites + group: example.upbound.io + names: + categories: + - websites + - composition + - crossplane + kind: XWebsite + plural: xwebsites + versions: + - name: v1alpha1 + additionalPrinterColumns: + - description: The main domain for the website. + jsonPath: .spec.parameters.domain.name + name: Domain + type: string + - description: The on/off switch for the website. + jsonPath: .spec.parameters.enabled + name: Enabled + type: boolean + served: true + referenceable: true + schema: + openAPIV3Schema: + type: object + properties: + spec: + type: object + properties: + parameters: + type: object + description: website configuration parameters. + properties: + region: + type: string + description: Region is the region youre bucket is located. + bucket: + description: "The initial bucket for the first required Origin. A distribution requires at least one origin.\n At the bare minimum a Website can simply server static files from a bucket." + properties: + name: + description: The name of the bucket with a static website. + type: string + path: + description: The path to the root dir in the Bucket which includes the main index.html + type: string + forceDestroy: + description: Boolean that indicates all objects (including any locked objects) should be deleted from the bucket when the bucket is destroyed so that the bucket can be destroyed without error. These objects are not recoverable. This only deletes objects when the bucket is destroyed, not when setting this parameter to true. If setting this field in the same operation that would require replacing the bucket or destroying the bucket, this flag will not work. + type: boolean + type: object + defaultRootObject: + default: index.html + description: The name of the file to load by default when the URL does not specify one. + type: string + description: + description: A descriptive blurb about what this Website is for. + type: string + domain: + description: domain configuration field + properties: + name: + description: domain name for a-record in zone + type: string + zoneId: + description: zoneId of the hosted zone to place your domain name. + type: string + type: object + enabled: + default: true + description: True when the distribution is active. Set to false before deleting a distribution. + type: boolean + tags: + description: Map of additional AWS Tags to add to resources + additionalProperties: + type: string + type: object + deletionPolicy: + type: string + description: DeletionPolicy specifies what will happen to the underlying external when this managed resource is deleted - either "Delete" or "Orphan" the external resource. + providerConfigRef: + type: object + description: crossplane provider credentials to use. + properties: + name: + type: string + required: + - name + required: + - parameters + - deletionPolicy + - providerConfigRef + status: + properties: + s3: + description: Useful values generated from the S3 API. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + certificate: + description: Useful values generated from the ACM API. + type: object + x-kubernetes-preserve-unknown-fields: true + cloudfront: + description: Useful values generated from the CloudFront API. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object diff --git a/apis/crossplane.yaml b/apis/crossplane.yaml new file mode 100644 index 0000000..d63f274 --- /dev/null +++ b/apis/crossplane.yaml @@ -0,0 +1,39 @@ +apiVersion: meta.pkg.crossplane.io/v1 +kind: Configuration +metadata: + name: platform-ref-s3-website + annotations: + meta.crossplane.io/maintainer: Upbound + meta.crossplane.io/source: github.com/upbound/platform-ref-s3-website + meta.crossplane.io/license: Apache-2.0 + meta.crossplane.io/description: | + This reference platform Configuration for Amazon S3 static website hosting + is a starting point to build, run, and operate Amazon S3 to host a static website. + On a static website, individual webpages include static content. + They might also contain client-side scripts. + meta.crossplane.io/readme: | + This reference platform Configuration for Amazon S3 static website hosting + is a starting point to build, run, and operate Amazon S3 to host a static website. + On a static website, individual webpages include static content. + They might also contain client-side scripts. + + This Configuration uses Amazon S3, CDN service primitives from the [Upbound Official AWS + Provider](https://marketplace.upbound.io/providers/upbound/provider-aws). + + To learn more checkout the [GitHub + repo](https://github.com/upbound/platform-ref-s3-website/) that you can copy and + customize to meet the exact needs of your organization! +spec: + crossplane: + version: ">=v1.12.1-0" + dependsOn: + - provider: xpkg.upbound.io/upbound/provider-family-aws + version: ">=v0.37.0" + - provider: xpkg.upbound.io/upbound/provider-aws-cloudfront + version: ">=v0.37.0" + - provider: xpkg.upbound.io/upbound/provider-aws-s3 + version: ">=v0.37.0" + - provider: xpkg.upbound.io/upbound/provider-aws-route53 + version: ">=v0.37.0" + - provider: xpkg.upbound.io/upbound/provider-aws-acm + version: ">=v0.37.0" diff --git a/build b/build new file mode 160000 index 0000000..bd5297b --- /dev/null +++ b/build @@ -0,0 +1 @@ +Subproject commit bd5297bd16c113cbc5ed1905b1d96aa1cb3078ec diff --git a/examples/content.yaml b/examples/content.yaml new file mode 100644 index 0000000..578dfe8 --- /dev/null +++ b/examples/content.yaml @@ -0,0 +1,26 @@ +apiVersion: example.upbound.io/v1alpha1 +kind: Content +metadata: + name: demo + namespace: demo-website +spec: + deletionPolicy: Delete + parameters: + bucket: + name: platform-ref-s3-website + path: /static + content: | + + + + +

The future of cloud is

+ up. + + + + region: us-east-1 + tags: + CostReference: PSP-12345 + providerConfigRef: + name: default diff --git a/examples/ns.yaml b/examples/ns.yaml new file mode 100644 index 0000000..c43ffde --- /dev/null +++ b/examples/ns.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: demo-website diff --git a/examples/provider-aws-scoped.yaml b/examples/provider-aws-scoped.yaml new file mode 100644 index 0000000..040d66b --- /dev/null +++ b/examples/provider-aws-scoped.yaml @@ -0,0 +1,34 @@ +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-family-aws +spec: + package: xpkg.upbound.io/upbound/provider-family-aws:v0.37.0 +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-cloudfront +spec: + package: xpkg.upbound.io/upbound/provider-aws-cloudfront:v0.37.0 +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-s3 +spec: + package: xpkg.upbound.io/upbound/provider-aws-s3:v0.37.0 +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-route53 +spec: + package: xpkg.upbound.io/upbound/provider-aws-route53:v0.37.0 +--- +apiVersion: pkg.crossplane.io/v1 +kind: Provider +metadata: + name: provider-aws-acm +spec: + package: xpkg.upbound.io/upbound/provider-aws-acm:v0.37.0 diff --git a/examples/providerconfig-creds.yaml b/examples/providerconfig-creds.yaml new file mode 100644 index 0000000..9211e6b --- /dev/null +++ b/examples/providerconfig-creds.yaml @@ -0,0 +1,11 @@ +apiVersion: aws.upbound.io/v1beta1 +kind: ProviderConfig +metadata: + name: default +spec: + credentials: + source: Secret + secretRef: + namespace: upbound-system + name: aws-creds + key: creds diff --git a/examples/website.yaml b/examples/website.yaml new file mode 100644 index 0000000..3530eed --- /dev/null +++ b/examples/website.yaml @@ -0,0 +1,23 @@ +apiVersion: example.upbound.io/v1alpha1 +kind: Website +metadata: + name: website + namespace: demo-website +spec: + deletionPolicy: Delete + parameters: + bucket: + name: platform-ref-s3-website + path: /static + forceDestroy: true + defaultRootObject: index.html + description: platform-ref-s3-website + domain: + name: demo.website.upboundrocks.cloud + zoneId: Z0115025YFQ7ZQF6BJ6N + enabled: true + region: us-east-1 + tags: + CostReference: PSP-12345 + providerConfigRef: + name: default diff --git a/examples/zone.yaml b/examples/zone.yaml new file mode 100644 index 0000000..45afd18 --- /dev/null +++ b/examples/zone.yaml @@ -0,0 +1,15 @@ +apiVersion: example.upbound.io/v1alpha1 +kind: DNSZone +metadata: + name: demo + namespace: demo-website +spec: + deletionPolicy: Orphan + parameters: + domain: + zoneName: website.example.cloud + region: us-east-1 + tags: + CostReference: PSP-12345 + providerConfigRef: + name: default diff --git a/test/setup.sh b/test/setup.sh new file mode 100755 index 0000000..5ebfb38 --- /dev/null +++ b/test/setup.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash +set -aeuo pipefail + +echo "Running setup.sh" +echo "Waiting until configuration package is installed..." +${KUBECTL} wait configuration.pkg platform-ref-s3-website --for=condition=Installed --timeout 5m +echo "Waiting until configuration package is healthy..." +${KUBECTL} wait configuration.pkg platform-ref-s3-website --for=condition=Healthy --timeout 5m + +echo "Creating cloud credential secret..." +${KUBECTL} -n upbound-system create secret generic aws-creds --from-literal=credentials="${UPTEST_CLOUD_CREDENTIALS}" \ + --dry-run=client -o yaml | ${KUBECTL} apply -f - + +echo "Waiting for all pods to come online..." +"${KUBECTL}" -n upbound-system wait --for=condition=Available deployment --all --timeout=5m + +echo "Waiting for all XRDs to be established..." +"${KUBECTL}" wait xrd --all --for condition=Established + +echo "Creating a default aws provider config..." +cat <