diff --git a/README.md b/README.md index 7fbb10f..f56086b 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,13 @@ Follow the instructions for a specific Service Instance below: ## Service Instances -| Type | Resource | Description | Status | -| ----------- | ----------- | ----------- | ----------- | -| [Amazon RDS](./amazon/ack/rds/README.md) | DBInstance | Create RDS instances | 🚧 Experimental | +| Type | Resource(s) | Description | Status | +| ------------------ | ---------------------------------- | -------------------------- | ---------------- | +| [Amazon RDS] | DBInstance | Create RDS instances | 🚧 Experimental | +| [Google Cloud SQL] | SQLInstance, SQLDatabase, SQLUser | Create Cloud SQL instances | 🚧 Experimental | +[Amazon RDS]: ./amazon/ack/rds/README.md +[Google Cloud SQL]: ./google/config-connector/cloudsql/README.md ## Building the Package Repository diff --git a/bundles/google/config-connector/cloudsql/bundle/.imgpkg/images.yml b/bundles/google/config-connector/cloudsql/bundle/.imgpkg/images.yml new file mode 100644 index 0000000..2992cde --- /dev/null +++ b/bundles/google/config-connector/cloudsql/bundle/.imgpkg/images.yml @@ -0,0 +1,3 @@ +--- +apiVersion: imgpkg.carvel.dev/v1alpha1 +kind: ImagesLock diff --git a/bundles/google/config-connector/cloudsql/bundle/config/00-schema.yml b/bundles/google/config-connector/cloudsql/bundle/config/00-schema.yml new file mode 100644 index 0000000..02579a6 --- /dev/null +++ b/bundles/google/config-connector/cloudsql/bundle/config/00-schema.yml @@ -0,0 +1,47 @@ +#@data/values-schema +--- +#@schema/title "Service Instance Name" +#@schema/desc "The name of the Cloud SQL instance and related objects" +name: "" + +#@schema/title "Service Instance Namespace" +#@schema/desc "The namespace the service instance objects should be deployed into" +#@schema/nullable +namespace: "" + +#@schmea/title "Database Version" +#@schema/desc "The database version of the Cloud SQL instance\nsee: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=Fields-,databaseVersion,-Optional" +version: POSTGRES_14 + +#@schema/title "Database Region" +#@schema/desc "The region this Cloud SQL instance should be deployed into\nsee: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=with%2Dobjects/namespaces/-,region,-Optional" +region: europe-west6 + +#@schema/title "Database Machine Type" +#@schema/desc "The machine type for the Cloud SQL instance\nsee: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=from%20your%20configuration.-,settings.tier,-Required" +tier: db-g1-small + +#@schema/title "Labels" +#@schema/desc "A set of labels which will be applied to all resources related to this instance" +#@schema/default {"app.kubernetes.io/component":"cloudsql-postgres"} +#@schema/examples ("Set custom labels on all objects", {"mycorp.io/service-type": "gcp-sql","mycorp.io/owner":"me@mycorp.io"}) +#@schema/type any=True +labels: + +#@schema/title "Service Instance Labels" +#@schema/desc "A set of labels which will be applied to the claimable secret" +#@schema/default {"services.apps.tanzu.vmware.com/class":"cloudsql-postgres"} +#@schema/type any=True +serviceInstanceLabels: + +#@schema/title "Allowed Networks" +#@schema/desc "A list of CIDR ranges allowed to talk to this Cloud SQL instance" +#@schema/examples ("Allow one (named) network & one host", [{"name": "my-onprem-net","value":"11.22.33.44/24"},{"value":"8.8.8.8/32"}]) +#@schema/nullable +allowedNetworks: +- + #@schema/desc "the name for the authorized/allowed network (optional)" + #@schema/nullable + name: "" + #@schema/desc "the IPv4 CIDR of the authorized/allowed network" + value: "" diff --git a/bundles/google/config-connector/cloudsql/bundle/config/10-instance.yml b/bundles/google/config-connector/cloudsql/bundle/config/10-instance.yml new file mode 100644 index 0000000..13da20d --- /dev/null +++ b/bundles/google/config-connector/cloudsql/bundle/config/10-instance.yml @@ -0,0 +1,239 @@ +#@ load("@ytt:data", "data") +#@ load("@ytt:assert", "assert") +#@ load("@ytt:overlay", "overlay") +#@ load("@ytt:template", "template") + + +#@ if data.values.name == "": +#@ assert.fail('data value "name" must not be empty') +#@ end +#@ instanceName = data.values.name +#@ namespace = data.values.namespace +#@ labels = data.values.labels +#@ siLabels = data.values.serviceInstanceLabels +#@ version = data.values.version +#@ region = data.values.region +#@ tier = data.values.tier +#@ allowedNetworks = data.values.allowedNetworks + +#! for now we only create one db and one user +#@ dbs = [ instanceName ] +#@ users = [ instanceName ] + +#@ objReader = '{}-obj-reader'.format(instanceName) + +#@ collectedPasswords = [] +#@ generateCredName = lambda user: '{}-{}-creds'.format(instanceName, user) + +#@ def generatePassword(name, len=64): +apiVersion: secretgen.k14s.io/v1alpha1 +kind: Password +metadata: + name: #@ name +spec: + length: #@ len + secretTemplate: + type: Opaque + stringData: + password: $(value) +#@ collectedPasswords.append(name) +#@ end + + +--- +#@ adminCredName = generateCredName("admin") +apiVersion: sql.cnrm.cloud.google.com/v1beta1 +kind: SQLInstance +metadata: + name: #@ instanceName +spec: + databaseVersion: #@ version + region: #@ region + rootPassword: + valueFrom: + secretKeyRef: + key: password + name: #@ adminCredName + settings: + activationPolicy: ALWAYS + availabilityType: ZONAL + backupConfiguration: + backupRetentionSettings: + retainedBackups: 1 + retentionUnit: COUNT + startTime: "16:00" + transactionLogRetentionDays: 1 + diskAutoresize: true + diskAutoresizeLimit: 0 + diskSize: 10 + diskType: PD_SSD + ipConfiguration: + #@ if allowedNetworks: + authorizedNetworks: #@ allowedNetworks + #@ end + ipv4Enabled: true + locationPreference: + zone: #@ '{}-a'.format(region) + pricingPlan: PER_USE + tier: #@ tier +--- #@ generatePassword(adminCredName) + +#@ for db in dbs: +--- +apiVersion: sql.cnrm.cloud.google.com/v1beta1 +kind: SQLDatabase +metadata: + name: #@ db +spec: + charset: UTF8 + collation: en_US.UTF8 + instanceRef: + name: #@ instanceName +#@ end + +#@ for user in users: +--- +#@ userCredName = generateCredName("user") +apiVersion: sql.cnrm.cloud.google.com/v1beta1 +kind: SQLUser +metadata: + name: #@ user +spec: + instanceRef: + name: #@ instanceName + password: + valueFrom: + secretKeyRef: + key: password + name: #@ userCredName +--- #@ generatePassword(userCredName) +#@ end + +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: #@ objReader + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: #@ objReader +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: #@ objReader +subjects: +- kind: ServiceAccount + name: #@ objReader + +--- +#! This can only happen after we've created all `Password`, otherwise +#! collectedPasswords won't be fully popoulated yet + +#@ objReaderVerbs = ["get"] +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: #@ objReader +rules: +- apiGroups: [ "" ] + resources: [ "secrets" ] + verbs: #@ objReaderVerbs + resourceNames: #@ collectedPasswords +- apiGroups: [ "sql.cnrm.cloud.google.com" ] + resources: [ "sqlinstances" ] + verbs: #@ objReaderVerbs + resourceNames: #@ [ instanceName ] +#@ if/end len(dbs) >= 1: +- apiGroups: [ "sql.cnrm.cloud.google.com" ] + resources: [ "sqldatabases" ] + verbs: #@ objReaderVerbs + resourceNames: #@ dbs +#@ if/end len(users) >= 1: +- apiGroups: [ "sql.cnrm.cloud.google.com" ] + resources: [ "sqlusers" ] + verbs: #@ objReaderVerbs + resourceNames: #@ users + +--- +#@ def generateSecretTemplate(instance, user, db): + +#! For now we deploy one instance with one database and one user. Thus we give +#! the SecretTemplate and therefor also the claimable secret the same name as +#! the instance itself. +#! Once we split that up and have multiple databases and/or users, we need to +#! come up with a naming strategy for the claimable secret. +#@ secTmplName = instance + +apiVersion: secretgen.carvel.dev/v1alpha1 +kind: SecretTemplate +metadata: + name: #@ secTmplName +spec: + inputResources: + - name: sqlInstance + ref: + apiVersion: sql.cnrm.cloud.google.com/v1beta1 + kind: SQLInstance + name: #@ instance + - name: sqlDatabase + ref: + apiVersion: sql.cnrm.cloud.google.com/v1beta1 + kind: SQLDatabase + name: #@ db + - name: sqlUser + ref: + apiVersion: sql.cnrm.cloud.google.com/v1beta1 + kind: SQLUser + name: #@ user + - name: sqlUserSecret + ref: + apiVersion: v1 + kind: Secret + name: $(.sqlUser.spec.password.valueFrom.secretKeyRef.name) + serviceAccountName: #@ objReader + template: + data: + password: $(.sqlUserSecret.data.password) + metadata: + labels: + #! services.apps.tanzu.vmware.com/CloudSQLInstance: $(.sqlInstance.metadata.name) + #! services.apps.tanzu.vmware.com/CloudSQLUser: $(.sqlUser.metadata.name) + #! services.apps.tanzu.vmware.com/CloudSQLDatabase: $(.sqlUser.metadata.name) + #! services.apps.tanzu.vmware.com/CloudSQLVersion: $(.sqlInstance.spec.databaseVersion) + app.kubernetes.io/instance: $(.sqlInstance.metadata.name) + _1: #@ template.replace(labels) + _2: #@ template.replace(siLabels) + stringData: + database: $(.sqlDatabase.metadata.name) + host: $(.sqlInstance.status.publicIpAddress) + port: "5432" + type: postgresql + username: $(.sqlUser.metadata.name) +#@ end + +#@ for user in users: +#@ for db in dbs: +--- #@ generateSecretTemplate(instanceName, user, db) +#@ end +#@ end + + +#@ objToLabelAndNamespace = overlay.not_op( +#@ overlay.or_op( +#@ overlay.subset({"apiVersion":"kapp.k14s.io/v1alpha1","kind":"Config"}), +#@ overlay.subset({"kind": "ImagesLock"}), +#@ ) +#@ ) +#@overlay/match expects="1+", by=objToLabelAndNamespace +--- +metadata: + #@overlay/match missing_ok=True + labels: #@ labels + + #@ if namespace not in [None, ""]: + #@overlay/match missing_ok=True + namespace: #@ namespace + #@ end diff --git a/bundles/google/config-connector/cloudsql/bundle/config/99-kapp-config.yml b/bundles/google/config-connector/cloudsql/bundle/config/99-kapp-config.yml new file mode 100644 index 0000000..4ecfaa5 --- /dev/null +++ b/bundles/google/config-connector/cloudsql/bundle/config/99-kapp-config.yml @@ -0,0 +1,24 @@ +--- +apiVersion: kapp.k14s.io/v1alpha1 +kind: Config +waitRules: +- supportsObservedGeneration: true + conditionMatchers: + - status: "True" + type: Ready + success: true + resourceMatchers: &cloudSqlResources + - apiVersionKindMatcher: {apiVersion: sql.cnrm.cloud.google.com/v1beta1, kind: SQLInstance} + - apiVersionKindMatcher: {apiVersion: sql.cnrm.cloud.google.com/v1beta1, kind: SQLDatabase} + - apiVersionKindMatcher: {apiVersion: sql.cnrm.cloud.google.com/v1beta1, kind: SQLUser} +rebaseRules: +- paths: + - [metadata, annotations, cnrm.cloud.google.com/management-conflict-prevention-policy] + - [metadata, annotations, cnrm.cloud.google.com/project-id] + - [metadata, annotations, cnrm.cloud.google.com/state-into-spec] + - [metadata, annotations, cnrm.cloud.google.com/observed-secret-versions] + - [metadata, annotations, cnrm.cloud.google.com/mutable-but-unreadable-fields] + - [spec, resourceID] + type: copy + sources: [new, existing] + resourceMatchers: *cloudSqlResources diff --git a/google/README.md b/google/README.md new file mode 100644 index 0000000..9400d15 --- /dev/null +++ b/google/README.md @@ -0,0 +1,4 @@ +# Google + +See: +- [Config Connector for Google Cloud SQL](./config-connector/cloudsql/README.md) diff --git a/google/config-connector/cloudsql/README.md b/google/config-connector/cloudsql/README.md new file mode 100644 index 0000000..1cffdec --- /dev/null +++ b/google/config-connector/cloudsql/README.md @@ -0,0 +1,37 @@ +# Google Config Connector - CloudSQL PSQL Instance + +Status: Experimental + +## Description + +This is a using the [Config Connector] to manage Google Cloud SQL PostrgreSQL +instances as a [Carvel Package]. + +[Config Connector]: https://cloud.google.com/config-connector/docs/overview +[Carvel Package]: https://carvel.dev/kapp-controller/docs/develop/packaging/ + + + + +## Bundle + +For more information on customizing the bundle see [here][bundle], specifically +the [values-schema]. Alternatively you can also see the configuration options +with the tanzu CLI once the [package repo has been installed][repo-install] on +your cluster: + +```shell +tanzu package available get \ + --values-schema psql.google.references.services.apps.tanzu.vmware.com/0.0.1-alpha +``` + +[bundle]: ../../../bundles/google/config-connector/cloudsql +[values-schema]: ../../../bundles/google/config-connector/cloudsql/bundle/config/00-schema.yml +[repo-install]: ../../../README.md#quick-start diff --git a/google/config-connector/cloudsql/package-install.yaml b/google/config-connector/cloudsql/package-install.yaml new file mode 100644 index 0000000..e3c4421 --- /dev/null +++ b/google/config-connector/cloudsql/package-install.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: packaging.carvel.dev/v1alpha1 +kind: PackageInstall +metadata: + name: cloudsql-1 + namespace: default +spec: + serviceAccountName: cloudsql-install + packageRef: + refName: psql.google.references.services.apps.tanzu.vmware.com + versionSelection: + constraints: 0.0.1-alpha + values: + - secretRef: + name: cloudsql-1-values +--- +apiVersion: v1 +kind: Secret +metadata: + name: cloudsql-1-values + namespace: default +stringData: + values.yml: | + name: "cloudsql-1" + namespace: "default" + allowedNetworks: + - name: service-instances-cluster + value: 34.65.178.24/32 diff --git a/google/config-connector/cloudsql/sa.yaml b/google/config-connector/cloudsql/sa.yaml new file mode 100644 index 0000000..962f397 --- /dev/null +++ b/google/config-connector/cloudsql/sa.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cloudsql-install + namespace: default +--- +#! : kapp inspect -a cloudsql-1-ctrl -n service-instances -t +#! +#! Namespace Name Kind Owner Rs Ri Age +#! service-instances cloudsql-1 SQLInstance kapp ok - 16h +#! service-instances cloudsql-1 SQLUser kapp ok - 16h +#! service-instances cloudsql-1-obj-reader ServiceAccount kapp ok - 16h +#! service-instances cloudsql-1-cloudsql-1-cloudsql-1 SecretTemplate kapp ok - 16h +#! service-instances cloudsql-1-cloudsql-1-creds Password kapp ok - 16h +#! service-instances L cloudsql-1-cloudsql-1-creds Secret cluster ok - 16h +#! service-instances cloudsql-1 SQLDatabase kapp ok - 16h +#! service-instances cloudsql-1-obj-reader Role kapp ok - 16h +#! service-instances cloudsql-1-admin-creds Password kapp ok - 16h +#! service-instances L cloudsql-1-admin-creds Secret cluster ok - 16h +#! service-instances cloudsql-1-obj-reader RoleBinding kapp ok - 16h + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cloudsql-install + namespace: default +rules: +- apiGroups: ["sql.cnrm.cloud.google.com"] + resources: ["sqlinstances","sqldatabases","sqlusers"] + verbs: ["*"] +- apiGroups: ["secretgen.carvel.dev", "secretgen.k14s.io"] + resources: ["secrettemplates","passwords"] + verbs: ["*"] +- apiGroups: [""] + resources: ["serviceaccounts","configmaps"] + verbs: ["*"] +- apiGroups: ["rbac.authorization.k8s.io"] + resources: ["roles","rolebindings"] + verbs: ["*"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: cloudsql-install + namespace: default +subjects: +- kind: ServiceAccount + name: cloudsql-install +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: cloudsql-install diff --git a/repository/packages/google/cloudsql/meta.yaml b/repository/packages/google/cloudsql/meta.yaml new file mode 100644 index 0000000..2a90874 --- /dev/null +++ b/repository/packages/google/cloudsql/meta.yaml @@ -0,0 +1,15 @@ +apiVersion: data.packaging.carvel.dev/v1alpha1 +kind: PackageMetadata +metadata: + name: psql.google.references.services.apps.tanzu.vmware.com +spec: + categories: + - services + displayName: Google Cloud SQL instance + longDescription: | + Install Google Cloud SQL instance + maintainers: + - name: The Services Toolkit team + providerName: VMware + shortDescription: cloudsql + supportDescription: Not supported - provided as reference package only diff --git a/repository/packages/google/cloudsql/package.yaml b/repository/packages/google/cloudsql/package.yaml new file mode 100644 index 0000000..1376d99 --- /dev/null +++ b/repository/packages/google/cloudsql/package.yaml @@ -0,0 +1,101 @@ +apiVersion: data.packaging.carvel.dev/v1alpha1 +kind: Package +metadata: + name: psql.google.references.services.apps.tanzu.vmware.com.0.0.1-alpha +spec: + refName: psql.google.references.services.apps.tanzu.vmware.com + version: 0.0.1-alpha + releasedAt: "2022-07-14T14:47:00+02:00" + releaseNotes: https://docs.vmware.com/en/Services-Toolkit-for-VMware-Tanzu/0.7/services-toolkit-0-7/GUID-overview.html + valuesSchema: + openAPIv3: + type: object + additionalProperties: false + properties: + name: + title: Service Instance Name + type: string + description: The name of the Cloud SQL instance and related objects + default: '' + namespace: + title: Service Instance Namespace + type: string + nullable: true + description: The namespace the service instance objects should be deployed into + default: null + version: + type: string + description: 'The database version of the Cloud SQL instance + + see: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=Fields-,databaseVersion,-Optional' + default: POSTGRES_14 + region: + title: Database Region + type: string + description: 'The region this Cloud SQL instance should be deployed into + + see: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=with%2Dobjects/namespaces/-,region,-Optional' + default: europe-west6 + tier: + title: Database Machine Type + type: string + description: 'The machine type for the Cloud SQL instance + + see: https://cloud.google.com/config-connector/docs/reference/resource-docs/sql/sqlinstance#:~:text=from%20your%20configuration.-,settings.tier,-Required' + default: db-g1-small + labels: + title: Labels + nullable: true + description: A set of labels which will be applied to all resources related to + this instance + x-example-description: Set custom labels on all objects + example: + mycorp.io/service-type: gcp-sql + mycorp.io/owner: me@mycorp.io + default: + app.kubernetes.io/component: cloudsql-postgres + serviceInstanceLabels: + title: Service Instance Labels + nullable: true + description: A set of labels which will be applied to the claimable secret + default: + services.apps.tanzu.vmware.com/class: cloudsql-postgres + allowedNetworks: + title: Allowed Networks + type: array + nullable: true + description: A list of CIDR ranges allowed to talk to this Cloud SQL instance + x-example-description: Allow one (named) network & one host + example: + - name: my-onprem-net + value: 11.22.33.44/24 + - value: 8.8.8.8/32 + items: + type: object + additionalProperties: false + properties: + name: + type: string + nullable: true + description: the name for the authorized/allowed network (optional) + default: null + value: + type: string + description: the IPv4 CIDR of the authorized/allowed network + default: '' + default: null + template: + spec: + fetch: + - imgpkgBundle: + image: ghcr.io/vmware-tanzu/tanzu-application-platform-reference-packages/psql.google.references.services.apps.tanzu.vmware.com@sha256:9fc1a2767adac504e06d6eb8c9b2cbecc887ad605e3652401f26dc101abd1c6d + template: + - ytt: + paths: + - config/ + - kbld: + paths: + - "-" + - ".imgpkg/images.yml" + deploy: + - kapp: {}