diff --git a/examples/postgres-operator/demo-with-expose-trait.yaml b/examples/postgres-operator/demo-with-expose-trait.yaml new file mode 100644 index 00000000..c38ef900 --- /dev/null +++ b/examples/postgres-operator/demo-with-expose-trait.yaml @@ -0,0 +1,17 @@ +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: postgres-operator-sample-with-postgres-expose-trait +spec: + components: + - type: "postgres-cluster" + name: "postgres" + namespace: "default" + properties: + replicas: 3 + traits: + - type: postgres-expose + properties: + type: NodePort + port: 5432 + targetPort: 5432 diff --git a/experimental/addons/postgres-operator/README.md b/experimental/addons/postgres-operator/README.md index ad3c8c42..c3e4be7f 100644 --- a/experimental/addons/postgres-operator/README.md +++ b/experimental/addons/postgres-operator/README.md @@ -51,6 +51,27 @@ spec: replicas: 3 ``` +If you want to create a service to access Postgres, So apply below YAML: + +```yaml +apiVersion: core.oam.dev/v1beta1 +kind: Application +metadata: + name: postgres-operator-sample +spec: + components: + - type: "postgres-cluster" + name: "postgres" + namespace: "default" + properties: + replicas: 3 # By default it's set to 2. + traits: + - type: postgres-expose + properties: + type: NodePort # Change this field if you want diffrent type of service. + port: [5432] +``` + ```shell $ kubectl get po -n prod -o wide NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES @@ -65,16 +86,16 @@ With a port-forward on one of the database pods (e.g. the master) you can connec ```shell # get name of master pod of acid-minimal-cluster -export PGMASTER=$(kubectl get pods -o jsonpath={.items..metadata.name} -l application=spilo,cluster-name=postgres,spilo-role=master -n prod) +$ export PGMASTER=$(kubectl get pods -n prod -o jsonpath={.items..metadata.name} -l application=spilo,cluster-name=postgres,spilo-role=master -n prod) # set up port forward -kubectl port-forward $PGMASTER 6432:5432 -n default +kubectl port-forward $PGMASTER -n prod 5432:5432 -n prod ``` Open another CLI and connect to the database using e.g. the psql client. When connecting with a manifest role like foo_user user, read its password from the K8s secret which was generated when creating acid-minimal-cluster. As non-encrypted connections are rejected by default set SSL mode to require: ```shell -export PGPASSWORD=$(kubectl get secret postgres.postgres.credentials.postgresql.acid.zalan.do -o 'jsonpath={.data.password}' | base64 -d) -export PGSSLMODE=require -psql -U postgres -h localhost -p 6432 +$ export PGPASSWORD=$(kubectl get secret -n prod postgres.postgres.credentials.postgresql.acid.zalan.do -o 'jsonpath={.data.password}' | base64 -d) +$ export PGSSLMODE=require +$ psql -U postgres -h localhost -p 5432 ``` diff --git a/experimental/addons/postgres-operator/definitions/postgres-cluster.cue b/experimental/addons/postgres-operator/definitions/postgres-cluster.cue index 9910d7d3..a33a0085 100644 --- a/experimental/addons/postgres-operator/definitions/postgres-cluster.cue +++ b/experimental/addons/postgres-operator/definitions/postgres-cluster.cue @@ -1,179 +1,179 @@ "postgres-cluster": { - alias: "" - annotations: {} - attributes: workload: type: "autodetects.core.oam.dev" - description: "postgres cluster component" - labels: {} - type: "component" + alias: "" + annotations: {} + attributes: workload: type: "autodetects.core.oam.dev" + description: "postgres cluster component" + labels: {} + type: "component" } template: { - output: { - kind: "postgresql" - apiVersion: "acid.zalan.do/v1" - metadata: { - name: context.name - namespace: context.namespace - // default namespace will be "prod" - } - spec: { - dockerImage: parameter.image //ghcr.io/zalando/spilo-15:2.1-p9 - numberOfInstances: parameter.replicas //By default it's 2 - teamId: parameter.teamId - postgresql: parameter.postgresql - databases: parameter.databases - preparedDatabases: parameter.preparedDatabases - users: parameter.users - enableMasterLoadBalancer: parameter.enableMasterLoadBalancer - enableReplicaLoadBalancer: parameter.enableReplicaLoadBalancer - enableConnectionPooler: parameter.enableConnectionPooler - enableReplicaConnectionPooler: parameter.enableReplicaConnectionPooler - enableMasterPoolerLoadBalancer: parameter.enableReplicaConnectionPooler - enableReplicaPoolerLoadBalancer: parameter.enableReplicaPoolerLoadBalancer - allowedSourceRanges: [ // load balancers' source ranges for both master and replica services - "127.0.0.1/32" - ] - volume: parameter.volume - additionalVolumes: [ - { - name: "empty" - mountPath: "/opt/empty" - targetContainers: [ - "all" - ] - volumeSource: { - emptyDir: {} - } - } - ] - enableShmVolume: parameter.enableShmVolume - resources: parameter.resources - patroni: parameter.patroni - ttl: parameter.ttl - loop_wait: parameter.loopWait - retry_timeout: parameter.retryTimeout - synchronous_mode: parameter.synchronousMode - synchronous_mode_strict: parameter.synchronousModeStrict - synchronous_node_count: parameter.synchronousNodeCount - maximum_lag_on_failover: 33554432 - initContainers: [ - { - name: "date" - image: "busybox" - command: [ "/bin/date" ] - } - ] - // Custom TLS certificate. Disabled unless tls.secretName has a value. - tls: parameter.tls - } - } - parameter: { - //+usage=configure postgresql. - postgresql: { - //+usage=the version of the postgresql to be used. - version: *"15" | string - parameters: { - // Expert section - shared_buffers: *"32MB" | string - max_connections: *"10" | string - log_statement: *"all" | string - } - } - //+usage=the size of the postgres cluster. - replicas: *2 | int - //+usage=set team Id. - teamId: *"acid" | string - //+usage=the image of the spilo. - image: *"ghcr.io/zalando/spilo-15:2.1-p9" | string - //+usage=configure volume. - volume: { - //+usage=the size of the volume used of postgres. - size: *"1Gi" | string - } - //+usage=define databases to be used. - databases: *{ - foo: "zalando" // dbname: owner - } | {...} - //+usage=configure created databases. - preparedDatabases: *{ - bar: { - defaultUsers: true - extensions: { - pg_partman: "public" - pgcrypto: "public" - } - schemas: { - data: {} - history: { - defaultRoles: true - defaultUsers: false - } - } - } - } | {...} - //+usage=configure users for the databases. - users: *{ - zalando: ["superuser", "createdb"] - foo_user: [] - } | {...} - //+usage=configure patroni. - patroni: { - failsafe_mode: *false | bool - initdb: { - encoding: *"UTF8" | string - locale: *"en_US.UTF-8" | string - "data-checksums": *"true" | string - } - } - //+usage=enable SHM volume if set true. - enableShmVolume: *true | bool - //+usage=enable master as load balancer if set true. - enableMasterLoadBalancer: *false | bool - //+usage=enable replica as load balancer if set true. - enableReplicaLoadBalancer: *false | bool - //+usage=enable/disable connection pooler deployment. - enableConnectionPooler: *false | bool - //+usage=set to enable connection pooler for replica service. - enableReplicaConnectionPooler: *false | bool - //+usage=set to enable master pooler as load balancer. - enableMasterPoolerLoadBalancer: *false | bool - //+usage=set to enable replica pooler as load balancer. - enableReplicaPoolerLoadBalancer: *false | bool - //+usage=set ttl(Time to live) by dedault it's 30 days. - ttl: *30 | int - //+usage=set loop wait time by dedault it's 10. - loopWait: *10 | int - //+usage=set retry timeout by dedault it's 10. - retryTimeout: *10 | int - //+usage=set to enable synchronous mode. - synchronousMode: *false | bool - //+usage=set to enable synchronous mode strictly. - synchronousModeStrict: *false | bool - //+usage=set how many nodes to be synchronized. - synchronousNodeCount: *1 | int - //+usage=configure resources. - resources: { - requests: { - cpu: *"10m" | string - memory: *"100Mi" | string - } - limits: { - cpu: *"500m" | string - memory: *"500Mi" | string - } - } - //+usage=configure custom TLS. - tls: { - //+usage=sets custom TLS secret name, It should correspond to a Kubernetes Secret resource to load. - secretName: *"" | string - //+usage=sets custom TLS certificate file. - certificateFile: *"tls.crt" | string - //+usage=sets custom TLS private key file. - privateKeyFile: *"tls.key" | string - //+usage=optionally configure Postgres with a CA certificate. - caFile: *"" | string - //+usage=optionally the ca.crt can come from this secret instead. - caSecretName: *"" | string - } - } + output: { + kind: "postgresql" + apiVersion: "acid.zalan.do/v1" + metadata: { + name: context.name + namespace: context.namespace + // default namespace will be "prod" + } + spec: { + dockerImage: parameter.image //ghcr.io/zalando/spilo-15:2.1-p9 + numberOfInstances: parameter.replicas //By default it's 2 + teamId: parameter.teamId + postgresql: parameter.postgresql + databases: parameter.databases + preparedDatabases: parameter.preparedDatabases + users: parameter.users + enableMasterLoadBalancer: parameter.enableMasterLoadBalancer + enableReplicaLoadBalancer: parameter.enableReplicaLoadBalancer + enableConnectionPooler: parameter.enableConnectionPooler + enableReplicaConnectionPooler: parameter.enableReplicaConnectionPooler + enableMasterPoolerLoadBalancer: parameter.enableReplicaConnectionPooler + enableReplicaPoolerLoadBalancer: parameter.enableReplicaPoolerLoadBalancer + allowedSourceRanges: [ // load balancers' source ranges for both master and replica services + "127.0.0.1/32", + ] + volume: parameter.volume + additionalVolumes: [ + { + name: "empty" + mountPath: "/opt/empty" + targetContainers: [ + "all", + ] + volumeSource: { + emptyDir: {} + } + }, + ] + enableShmVolume: parameter.enableShmVolume + resources: parameter.resources + patroni: parameter.patroni + ttl: parameter.ttl + loop_wait: parameter.loopWait + retry_timeout: parameter.retryTimeout + synchronous_mode: parameter.synchronousMode + synchronous_mode_strict: parameter.synchronousModeStrict + synchronous_node_count: parameter.synchronousNodeCount + maximum_lag_on_failover: 33554432 + initContainers: [ + { + name: "date" + image: "busybox" + command: [ "/bin/date"] + }, + ] + // Custom TLS certificate. Disabled unless tls.secretName has a value. + tls: parameter.tls + } + } + parameter: { + //+usage=configure postgresql. + postgresql: { + //+usage=the version of the postgresql to be used. + version: *"15" | string + parameters: { + // Expert section + shared_buffers: *"32MB" | string + max_connections: *"10" | string + log_statement: *"all" | string + } + } + //+usage=the size of the postgres cluster. + replicas: *2 | int + //+usage=set team Id. + teamId: *"acid" | string + //+usage=the image of the spilo. + image: *"ghcr.io/zalando/spilo-15:2.1-p9" | string + //+usage=configure volume. + volume: { + //+usage=the size of the volume used of postgres. + size: *"1Gi" | string + } + //+usage=define databases to be used. + databases: *{ + foo: "zalando" // dbname: owner + } | {...} + //+usage=configure created databases. + preparedDatabases: *{ + bar: { + defaultUsers: true + extensions: { + pg_partman: "public" + pgcrypto: "public" + } + schemas: { + data: {} + history: { + defaultRoles: true + defaultUsers: false + } + } + } + } | {...} + //+usage=configure users for the databases. + users: *{ + zalando: ["superuser", "createdb"] + foo_user: [] + } | {...} + //+usage=configure patroni. + patroni: { + failsafe_mode: *false | bool + initdb: { + encoding: *"UTF8" | string + locale: *"en_US.UTF-8" | string + "data-checksums": *"true" | string + } + } + //+usage=enable SHM volume if set true. + enableShmVolume: *true | bool + //+usage=enable master as load balancer if set true. + enableMasterLoadBalancer: *false | bool + //+usage=enable replica as load balancer if set true. + enableReplicaLoadBalancer: *false | bool + //+usage=enable/disable connection pooler deployment. + enableConnectionPooler: *false | bool + //+usage=set to enable connection pooler for replica service. + enableReplicaConnectionPooler: *false | bool + //+usage=set to enable master pooler as load balancer. + enableMasterPoolerLoadBalancer: *false | bool + //+usage=set to enable replica pooler as load balancer. + enableReplicaPoolerLoadBalancer: *false | bool + //+usage=set ttl(Time to live) by dedault it's 30 days. + ttl: *30 | int + //+usage=set loop wait time by dedault it's 10. + loopWait: *10 | int + //+usage=set retry timeout by dedault it's 10. + retryTimeout: *10 | int + //+usage=set to enable synchronous mode. + synchronousMode: *false | bool + //+usage=set to enable synchronous mode strictly. + synchronousModeStrict: *false | bool + //+usage=set how many nodes to be synchronized. + synchronousNodeCount: *1 | int + //+usage=configure resources. + resources: { + requests: { + cpu: *"10m" | string + memory: *"100Mi" | string + } + limits: { + cpu: *"500m" | string + memory: *"500Mi" | string + } + } + //+usage=configure custom TLS. + tls: { + //+usage=sets custom TLS secret name, It should correspond to a Kubernetes Secret resource to load. + secretName: *"" | string + //+usage=sets custom TLS certificate file. + certificateFile: *"tls.crt" | string + //+usage=sets custom TLS private key file. + privateKeyFile: *"tls.key" | string + //+usage=optionally configure Postgres with a CA certificate. + caFile: *"" | string + //+usage=optionally the ca.crt can come from this secret instead. + caSecretName: *"" | string + } + } } diff --git a/experimental/addons/postgres-operator/definitions/traits/postgres-expose.cue b/experimental/addons/postgres-operator/definitions/traits/postgres-expose.cue new file mode 100644 index 00000000..191323f6 --- /dev/null +++ b/experimental/addons/postgres-operator/definitions/traits/postgres-expose.cue @@ -0,0 +1,80 @@ +"postgres-expose": { + alias: "" + annotations: {} + attributes: { + workload: type: "autodetects.core.oam.dev" + } + description: "postgres expose trait" + labels: {} + type: "trait" +} + +template: { + outputs: service: { + apiVersion: "v1" + kind: "Service" + metadata: annotations: parameter.annotations + spec: { + selector: { + "application": "spilo" + "cluster-name": "postgres" + "spilo-role": "master" + "team": "acid" + } + // compatible with the old way + if parameter["port"] != _|_ if parameter["ports"] == _|_ { + ports: [ + for p in parameter.port { + name: "port" + port: p + targetPort: p + }, + ] + } + if parameter["ports"] != _|_ { + ports: [ for v in parameter.ports { + port: v.port + targetPort: v.port + if v.name != _|_ { + name: v.name + } + if v.name == _|_ { + _name: "port" + name: *_name | string + if v.protocol != "TCP" { + name: _name + } + } + if v.nodePort != _|_ if parameter.type == "NodePort" { + nodePort: v.nodePort + } + if v.protocol != _|_ { + protocol: v.protocol + } + }, + ] + } + type: parameter.type + } + } + parameter: { + // +usage=Deprecated, the old way to specify the exposion ports + port?: [...int] + // +usage=Specify portsyou want customer traffic sent to + ports?: [...{ + // +usage=Number of port to expose on the pod's IP address + port: int + // +usage=Name of the port + name?: string + // +usage=Protocol for port. Must be UDP, TCP, or SCTP + protocol: *"TCP" | "UDP" | "SCTP" + // +usage=exposed node port. Only Valid when exposeType is NodePort + nodePort?: int + }] + // +usage=Specify the annotaions of the exposed service + annotations: [string]: string + matchLabels?: [string]: string + // +usage=Specify what kind of Service you want. options: "ClusterIP","NodePort","LoadBalancer","ExternalName" + type: *"ClusterIP" | "NodePort" | "LoadBalancer" | "ExternalName" + } +} diff --git a/experimental/addons/postgres-operator/metadata.yaml b/experimental/addons/postgres-operator/metadata.yaml index 5cac4c1f..b4d54e7f 100644 --- a/experimental/addons/postgres-operator/metadata.yaml +++ b/experimental/addons/postgres-operator/metadata.yaml @@ -1,5 +1,5 @@ name: postgres-operator -version: 1.0.0 +version: 1.0.1 description: This addon adds postgres-operator to the kubevela to be used by across the application installed in kubevela. icon: "" url: "https://www.postgresql.org/"