diff --git a/.github/workflows/scheduled-generated.yml b/.github/workflows/scheduled-generated.yml index 18fbb971..5bd3f521 100644 --- a/.github/workflows/scheduled-generated.yml +++ b/.github/workflows/scheduled-generated.yml @@ -21,6 +21,8 @@ jobs: run: | git remote add -f -t main --no-tags examples https://github.com/score-spec/examples.git git remote add -f -t main --no-tags community-provisioners https://github.com/score-spec/community-provisioners.git + git remote add -f -t main --no-tags score-compose https://github.com/score-spec/score-compose.git + git remote add -f -t main --no-tags score-k8s https://github.com/score-spec/score-k8s.git git remote add -f -t main --no-tags patch-templates https://github.com/score-spec/community-patchers.git # Set github actions bot as the committer, see https://github.com/actions/checkout/pull/1184 diff --git a/README.md b/README.md index 14769eee..4fa9dad5 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,15 @@ The commands for the initial integration of the repos are: ```bash git remote add -f -t main --no-tags examples https://github.com/score-spec/examples.git git remote add -f -t main --no-tags community-provisioners https://github.com/score-spec/community-provisioners.git +git remote add -f -t main --no-tags score-compose https://github.com/score-spec/score-compose.git +git remote add -f -t main --no-tags score-k8s https://github.com/score-spec/score-k8s.git git remote add -f -t main --no-tags patch-templates https://github.com/score-spec/community-patchers.git git read-tree --prefix=gen/external-content/score/specification -u examples/main:specification +git read-tree --prefix=gen/external-content/resource-provisioners/default/score-compose -u score-compose/main:internal/command git read-tree --prefix=gen/external-content/score/resources/default-provisioners -u examples/main:resources git read-tree --prefix=gen/external-content/score/resources/community-provisioners -u community-provisioners/main +git read-tree --prefix=gen/external-content/resource-provisioners/default/score-k8s -u score-k8s/main:internal/provisioners/default +git read-tree --prefix=gen/external-content/resource-provisioners/community -u community-provisioners/main git read-tree --prefix=gen/external-content/patch-templates -u patch-templates/main git add gen/external-content git commit -s -S -m "Integrating external content" diff --git a/config.toml b/config.toml index 56350b28..59729f27 100644 --- a/config.toml +++ b/config.toml @@ -163,12 +163,18 @@ url = "https://github.com/score-spec/examples/blob/main" name = "score/resources/community-provisioners" url = "https://github.com/score-spec/community-provisioners/blob/main" [[Params.exampleLibraryGitHubUrls]] +name = "resource-provisioners/community" +url = "https://github.com/score-spec/community-provisioners/blob/main" +[[Params.exampleLibraryGitHubUrls]] name = "patch-templates" url = "https://github.com/score-spec/community-patchers/blob/main" [[Params.exampleTypeLabels]] name = "score" labels = ["Specification", "Resources", "Provisioner"] [[Params.exampleTypeLabels]] +name = "resource-provisioners" +labels = ["Source", "Implementation", "Provisioner Type", "Resource Type", "Flavor", "Tool"] +[[Params.exampleTypeLabels]] name = "patch-templates" labels = ["Implementation"] diff --git a/content/en/examples/_index.md b/content/en/examples/_index.md index 7a124acd..f2c5700c 100644 --- a/content/en/examples/_index.md +++ b/content/en/examples/_index.md @@ -21,6 +21,10 @@ Explore a curated set of example files covering a range of entities for working {{< link-card-item text="Find some Score files examples illustrating how to use the Score specification as well as how to use the resources provisioners with both `score-compose` and `score-k8s`." url="/examples/score" linkLabel="Get examples" >}} {{< /multi-link-card >}} +{{< multi-link-card title="Resources provisioners" >}} +{{< link-card-item text="Find some Score files examples illustrating how to use the resources provisioners with both `score-compose` and `score-k8s`." url="/examples/resource-provisioners" linkLabel="Get examples" >}} +{{< /multi-link-card >}} + {{< multi-link-card title="Patch templates" >}} {{< link-card-item text="Find some examples illustrating how to use the patch templates with both `score-compose` and `score-k8s`." url="/examples/patch-templates" linkLabel="Get examples" >}} {{< /multi-link-card >}} diff --git a/content/en/examples/resource-provisioners/_index.md b/content/en/examples/resource-provisioners/_index.md new file mode 100644 index 00000000..ac4b6e62 --- /dev/null +++ b/content/en/examples/resource-provisioners/_index.md @@ -0,0 +1,9 @@ +--- +title: "Resource Provisioners" +draft: false +type: examples +--- + +The examples below illustrate how to use resources provisioners for each Score implementation. + +--- diff --git a/content/en/examples/resource-provisioners/community/dapr-pubsub/score-compose/template/redis-dapr-pubsub.md b/content/en/examples/resource-provisioners/community/dapr-pubsub/score-compose/template/redis-dapr-pubsub.md new file mode 100644 index 00000000..02cfe203 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-pubsub/score-compose/template/redis-dapr-pubsub.md @@ -0,0 +1,21 @@ +--- +title: "redis-dapr-pubsub" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "dapr-pubsub" +provisionerType: "template" +flavor: "redis" +excerpt: '' +description: 'Generates a Dapr PubSub Component pointing to a Redis Service.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr PubSub Component pointing to a Redis Service." type="dapr-pubsub" expectedOutputs="name" %}} + +{{% example-file filename="10-redis-dapr-pubsub.provisioners.yaml" dir="resource-provisioners/community/dapr-pubsub/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dapr-pubsub/score-k8s/template/rabbitmq-dapr-pubsub.md b/content/en/examples/resource-provisioners/community/dapr-pubsub/score-k8s/template/rabbitmq-dapr-pubsub.md new file mode 100644 index 00000000..c16a98f5 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-pubsub/score-k8s/template/rabbitmq-dapr-pubsub.md @@ -0,0 +1,21 @@ +--- +title: "rabbitmq-dapr-pubsub" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "dapr-pubsub" +provisionerType: "template" +flavor: "rabbitmq" +excerpt: '' +description: 'Generates a Dapr PubSub Component pointing to a RabbitMQ StatefulSet.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr PubSub Component pointing to a RabbitMQ StatefulSet." type="dapr-pubsub" expectedOutputs="name" %}} + +{{% example-file filename="10-rabbitmq-dapr-pubsub.provisioners.yaml" dir="resource-provisioners/community/dapr-pubsub/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dapr-pubsub/score-k8s/template/redis-dapr-pubsub.md b/content/en/examples/resource-provisioners/community/dapr-pubsub/score-k8s/template/redis-dapr-pubsub.md new file mode 100644 index 00000000..d3e7877b --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-pubsub/score-k8s/template/redis-dapr-pubsub.md @@ -0,0 +1,21 @@ +--- +title: "redis-dapr-pubsub" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "dapr-pubsub" +provisionerType: "template" +flavor: "redis" +excerpt: '' +description: 'Generates a Dapr PubSub Component pointing to a Redis StatefulSet.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr PubSub Component pointing to a Redis StatefulSet." type="dapr-pubsub" expectedOutputs="name" %}} + +{{% example-file filename="10-redis-dapr-pubsub.provisioners.yaml" dir="resource-provisioners/community/dapr-pubsub/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dapr-state-store/score-compose/template/redis-dapr-state-store.md b/content/en/examples/resource-provisioners/community/dapr-state-store/score-compose/template/redis-dapr-state-store.md new file mode 100644 index 00000000..b0e3ec6f --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-state-store/score-compose/template/redis-dapr-state-store.md @@ -0,0 +1,21 @@ +--- +title: "redis-dapr-state-store" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "dapr-state-store" +provisionerType: "template" +flavor: "redis" +excerpt: '' +description: 'Generates a Dapr StateStore Component pointing to a Redis Service.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr StateStore Component pointing to a Redis Service." type="dapr-state-store" expectedOutputs="name" %}} + +{{% example-file filename="10-redis-dapr-state-store.provisioners.yaml" dir="resource-provisioners/community/dapr-state-store/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dapr-state-store/score-k8s/template/redis.md b/content/en/examples/resource-provisioners/community/dapr-state-store/score-k8s/template/redis.md new file mode 100644 index 00000000..2fc7d8e0 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-state-store/score-k8s/template/redis.md @@ -0,0 +1,21 @@ +--- +title: "redis" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "dapr-state-store" +provisionerType: "template" +flavor: "redis" +excerpt: '' +description: 'Generates a Dapr StateStore Component pointing to a Redis StatefulSet.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr StateStore Component pointing to a Redis StatefulSet." type="dapr-state-store" expectedOutputs="name" %}} + +{{% example-file filename="10-redis-dapr-state-store.provisioners.yaml" dir="resource-provisioners/community/dapr-state-store/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dapr-subscription/score-compose/template/dapr-subscription.md b/content/en/examples/resource-provisioners/community/dapr-subscription/score-compose/template/dapr-subscription.md new file mode 100644 index 00000000..6d9dd3f8 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-subscription/score-compose/template/dapr-subscription.md @@ -0,0 +1,25 @@ +--- +title: "dapr-subscription" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "dapr-subscription" +provisionerType: "template" +flavor: "dapr" +excerpt: '' +description: 'Generates a Dapr Subscription on a given Topic and PubSub.' +expectedOutputs: + - name + - topic +supportedParams: + - topic + - pubsub +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr Subscription on a given Topic and PubSub." type="dapr-subscription" supportedParams="topic,pubsub" expectedOutputs="name,topic" %}} + +{{% example-file filename="10-dapr-subscription.provisioners.yaml" dir="resource-provisioners/community/dapr-subscription/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dapr-subscription/score-k8s/template/dapr-subscription.md b/content/en/examples/resource-provisioners/community/dapr-subscription/score-k8s/template/dapr-subscription.md new file mode 100644 index 00000000..1bbd69d9 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dapr-subscription/score-k8s/template/dapr-subscription.md @@ -0,0 +1,25 @@ +--- +title: "dapr-subscription" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "dapr-subscription" +provisionerType: "template" +flavor: "dapr" +excerpt: '' +description: 'Generates a Dapr Subscription on a given Topic and PubSub.' +expectedOutputs: + - name + - topic +supportedParams: + - topic + - pubsub +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates a Dapr Subscription on a given Topic and PubSub." type="dapr-subscription" supportedParams="topic,pubsub" expectedOutputs="name,topic" %}} + +{{% example-file filename="10-dapr-subscription.provisioners.yaml" dir="resource-provisioners/community/dapr-subscription/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dns/score-compose/cmd/dns-in-codespace.md b/content/en/examples/resource-provisioners/community/dns/score-compose/cmd/dns-in-codespace.md new file mode 100644 index 00000000..7243aa16 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dns/score-compose/cmd/dns-in-codespace.md @@ -0,0 +1,28 @@ +--- +title: "dns-in-codespace" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "dns" +provisionerType: "cmd" +flavor: "dns-in-codespace" +excerpt: 'Prerequisites for `dns-in-codespace`: +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace.' +description: 'Get the forwarded port URL in current GitHub Codespace on port 8080' +expectedOutputs: + - host + - url +tool: dns +hasMore: false + +--- + +Prerequisites for `dns-in-codespace`: + +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace. + +{{% resource-provisioner-content description="Get the forwarded port URL in current GitHub Codespace on port 8080" type="dns" expectedOutputs="host,url" %}} + +{{% example-file filename="10-dns-in-codespace.provisioners.yaml" dir="resource-provisioners/community/dns/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dns/score-compose/template/dns-with-route.md b/content/en/examples/resource-provisioners/community/dns/score-compose/template/dns-with-route.md new file mode 100644 index 00000000..a37afe35 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dns/score-compose/template/dns-with-route.md @@ -0,0 +1,27 @@ +--- +title: "dns-with-route" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "dns" +provisionerType: "template" +flavor: "dns" +excerpt: 'Prerequisites for `dns-in-codespace`: +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace.' +description: 'Outputs a *.localhost domain as the hostname and associated URL in http on port 8080' +expectedOutputs: + - host + - url +hasMore: false + +--- + +Prerequisites for `dns-in-codespace`: + +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace. + +{{% resource-provisioner-content description="Outputs a *.localhost domain as the hostname and associated URL in http on port 8080" type="dns" expectedOutputs="host,url" %}} + +{{% example-file filename="10-dns-with-url.provisioners.yaml" dir="resource-provisioners/community/dns/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dns/score-k8s/cmd/dns-in-codespace.md b/content/en/examples/resource-provisioners/community/dns/score-k8s/cmd/dns-in-codespace.md new file mode 100644 index 00000000..c67b77af --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dns/score-k8s/cmd/dns-in-codespace.md @@ -0,0 +1,28 @@ +--- +title: "dns-in-codespace" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "dns" +provisionerType: "cmd" +flavor: "dns-in-codespace" +excerpt: 'Prerequisites for `dns-in-codespace`: +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace.' +description: 'Get the forwarded port URL in current GitHub Codespace on port 80' +expectedOutputs: + - host + - url +tool: dns +hasMore: false + +--- + +Prerequisites for `dns-in-codespace`: + +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace. + +{{% resource-provisioner-content description="Get the forwarded port URL in current GitHub Codespace on port 80" type="dns" expectedOutputs="host,url" %}} + +{{% example-file filename="10-dns-in-codespace.provisioners.yaml" dir="resource-provisioners/community/dns/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/dns/score-k8s/template/dns-with-url.md b/content/en/examples/resource-provisioners/community/dns/score-k8s/template/dns-with-url.md new file mode 100644 index 00000000..798310b5 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/dns/score-k8s/template/dns-with-url.md @@ -0,0 +1,27 @@ +--- +title: "dns-with-url" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "dns" +provisionerType: "template" +flavor: "dns" +excerpt: 'Prerequisites for `dns-in-codespace`: +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace.' +description: 'Outputs a *.localhost domain as the hostname and associated URL in http on port 80' +expectedOutputs: + - host + - url +hasMore: false + +--- + +Prerequisites for `dns-in-codespace`: + +- Have `gh` installed, this provisioner is using the GitHub CLI to get the name of the current GitHub Codespace. + +{{% resource-provisioner-content description="Outputs a *.localhost domain as the hostname and associated URL in http on port 80" type="dns" expectedOutputs="host,url" %}} + +{{% example-file filename="10-dns-with-url.provisioners.yaml" dir="resource-provisioners/community/dns/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/environment/score-compose/cmd/dotenv.md b/content/en/examples/resource-provisioners/community/environment/score-compose/cmd/dotenv.md new file mode 100644 index 00000000..af61b9af --- /dev/null +++ b/content/en/examples/resource-provisioners/community/environment/score-compose/cmd/dotenv.md @@ -0,0 +1,25 @@ +--- +title: "dotenv" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "environment" +provisionerType: "cmd" +flavor: "dotenv" +excerpt: 'Prerequisites: +- Have `python` installed, this provisioner is using Python to load the `.env` file.' +description: 'Loads environment variables from a local .env file.' +tool: dotenv +hasMore: false + +--- + +Prerequisites: + +- Have `python` installed, this provisioner is using Python to load the `.env` file. + +{{% resource-provisioner-content description="Loads environment variables from a local .env file." type="environment" %}} + +{{% example-file filename="10-env.provisioners.yaml" dir="resource-provisioners/community/environment/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/environment/score-k8s/cmd/dotenv.md b/content/en/examples/resource-provisioners/community/environment/score-k8s/cmd/dotenv.md new file mode 100644 index 00000000..43586954 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/environment/score-k8s/cmd/dotenv.md @@ -0,0 +1,25 @@ +--- +title: "dotenv" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "environment" +provisionerType: "cmd" +flavor: "dotenv" +excerpt: 'Prerequisites: +- Have `python` installed, this provisioner is using Python to load the `.env` file.' +description: 'Loads environment variables from a local .env file.' +tool: dotenv +hasMore: false + +--- + +Prerequisites: + +- Have `python` installed, this provisioner is using Python to load the `.env` file. + +{{% resource-provisioner-content description="Loads environment variables from a local .env file." type="environment" %}} + +{{% example-file filename="10-env.provisioners.yaml" dir="resource-provisioners/community/environment/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/horizontal-pod-autoscaler/score-compose/template/empty-hpa.md b/content/en/examples/resource-provisioners/community/horizontal-pod-autoscaler/score-compose/template/empty-hpa.md new file mode 100644 index 00000000..b7c4e023 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/horizontal-pod-autoscaler/score-compose/template/empty-hpa.md @@ -0,0 +1,19 @@ +--- +title: "empty-hpa" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "horizontal-pod-autoscaler" +provisionerType: "template" +flavor: "empty" +excerpt: '' +description: 'Generates an empty object because HPA is not supported in Docker Compose.' +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates an empty object because HPA is not supported in Docker Compose." type="horizontal-pod-autoscaler" %}} + +{{% example-file filename="10-hpa.provisioners.yaml" dir="resource-provisioners/community/horizontal-pod-autoscaler/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/horizontal-pod-autoscaler/score-k8s/template/default-hpa.md b/content/en/examples/resource-provisioners/community/horizontal-pod-autoscaler/score-k8s/template/default-hpa.md new file mode 100644 index 00000000..1c0168c3 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/horizontal-pod-autoscaler/score-k8s/template/default-hpa.md @@ -0,0 +1,23 @@ +--- +title: "default-hpa" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "horizontal-pod-autoscaler" +provisionerType: "template" +flavor: "default" +excerpt: '' +description: 'Generates an HorizontalPodAutoscaler manifest.' +supportedParams: + - maxReplicas + - minReplicas + - targetCPUUtilizationPercentage +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates an HorizontalPodAutoscaler manifest." type="horizontal-pod-autoscaler" supportedParams="maxReplicas,minReplicas,targetCPUUtilizationPercentage" %}} + +{{% example-file filename="10-hpa.provisioners.yaml" dir="resource-provisioners/community/horizontal-pod-autoscaler/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/redis/score-k8s/cmd/helm-template-redis.md b/content/en/examples/resource-provisioners/community/redis/score-k8s/cmd/helm-template-redis.md new file mode 100644 index 00000000..abce3ad8 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/redis/score-k8s/cmd/helm-template-redis.md @@ -0,0 +1,42 @@ +--- +title: "helm-template-redis" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "redis" +provisionerType: "cmd" +flavor: "helm-template-redis" +excerpt: 'Prerequisites: +- Have `helm` installed locally, this provisioner renders the manifests from the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have `yq` installed locally.' +description: 'Generates the manifests of the bitnami/redis Helm chart.' +expectedOutputs: + - host + - port + - username + - password +tool: helm +hasMore: true + +--- + +## For `10-redis-helm-template.provisioners.yaml` + +Prerequisites: + +- Have `helm` installed locally, this provisioner renders the manifests from the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have `yq` installed locally. + +## For `10-redis-helm-upgrade.provisioners.yaml` + +Prerequisites: + +- Have `helm` installed locally, this provisioner installs the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have access to a cluster where the Helm chart will be installed. + - If you don't have one, you can deploy a `Kind` cluster locally by running this script: `.scripts/setup-kind-cluster.sh`. + +{{% resource-provisioner-content description="Generates the manifests of the bitnami/redis Helm chart." type="redis" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="10-redis-helm-template.provisioners.yaml" dir="resource-provisioners/community/redis/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/redis/score-k8s/cmd/helm-upgrade-redis.md b/content/en/examples/resource-provisioners/community/redis/score-k8s/cmd/helm-upgrade-redis.md new file mode 100644 index 00000000..223eabd6 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/redis/score-k8s/cmd/helm-upgrade-redis.md @@ -0,0 +1,42 @@ +--- +title: "helm-upgrade-redis" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "redis" +provisionerType: "cmd" +flavor: "helm-upgrade-redis" +excerpt: 'Prerequisites: +- Have `helm` installed locally, this provisioner renders the manifests from the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have `yq` installed locally.' +description: 'Deploys the bitnami/redis Helm chart in an existing cluster.' +expectedOutputs: + - host + - port + - username + - password +tool: helm +hasMore: true + +--- + +## For `10-redis-helm-template.provisioners.yaml` + +Prerequisites: + +- Have `helm` installed locally, this provisioner renders the manifests from the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have `yq` installed locally. + +## For `10-redis-helm-upgrade.provisioners.yaml` + +Prerequisites: + +- Have `helm` installed locally, this provisioner installs the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have access to a cluster where the Helm chart will be installed. + - If you don't have one, you can deploy a `Kind` cluster locally by running this script: `.scripts/setup-kind-cluster.sh`. + +{{% resource-provisioner-content description="Deploys the bitnami/redis Helm chart in an existing cluster." type="redis" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="10-redis-helm-upgrade.provisioners.yaml" dir="resource-provisioners/community/redis/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/route/score-k8s/template/ingress-route.md b/content/en/examples/resource-provisioners/community/route/score-k8s/template/ingress-route.md new file mode 100644 index 00000000..e1e27f06 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/route/score-k8s/template/ingress-route.md @@ -0,0 +1,23 @@ +--- +title: "ingress-route" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "route" +provisionerType: "template" +flavor: "ingress" +excerpt: '' +description: 'Provisions an Ingress route on a shared nginx instance.' +supportedParams: + - path + - host + - port +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions an Ingress route on a shared nginx instance." type="route" supportedParams="path,host,port" %}} + +{{% example-file filename="10-ingress-route.provisioners.yaml" dir="resource-provisioners/community/route/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/route/score-k8s/template/ingress-with-net-pol-route.md b/content/en/examples/resource-provisioners/community/route/score-k8s/template/ingress-with-net-pol-route.md new file mode 100644 index 00000000..e0c9f92b --- /dev/null +++ b/content/en/examples/resource-provisioners/community/route/score-k8s/template/ingress-with-net-pol-route.md @@ -0,0 +1,23 @@ +--- +title: "ingress-with-net-pol-route" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "route" +provisionerType: "template" +flavor: "ingress" +excerpt: '' +description: 'Provisions an Ingress route on a shared nginx instance, and a NetworkPolicy between them.' +supportedParams: + - path + - host + - port +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions an Ingress route on a shared nginx instance, and a NetworkPolicy between them." type="route" supportedParams="path,host,port" %}} + +{{% example-file filename="10-ingress-with-netpol-route.provisioners.yaml" dir="resource-provisioners/community/route/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/route/score-k8s/template/route-with-shared-gateway-with-netpol.md b/content/en/examples/resource-provisioners/community/route/score-k8s/template/route-with-shared-gateway-with-netpol.md new file mode 100644 index 00000000..bdbfd20e --- /dev/null +++ b/content/en/examples/resource-provisioners/community/route/score-k8s/template/route-with-shared-gateway-with-netpol.md @@ -0,0 +1,23 @@ +--- +title: "route-with-shared-gateway-with-netpol" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "route" +provisionerType: "template" +flavor: "route" +excerpt: '' +description: 'Generates an HTTPRoute attached to a default Gateway in default Namespace, and a NetworkPolicy between them.' +supportedParams: + - path + - host + - port +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates an HTTPRoute attached to a default Gateway in default Namespace, and a NetworkPolicy between them." type="route" supportedParams="path,host,port" %}} + +{{% example-file filename="10-shared-gateway-httproute-with-netpol.provisioners.yaml" dir="resource-provisioners/community/route/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/route/score-k8s/template/route-with-shared-gateway.md b/content/en/examples/resource-provisioners/community/route/score-k8s/template/route-with-shared-gateway.md new file mode 100644 index 00000000..9541839e --- /dev/null +++ b/content/en/examples/resource-provisioners/community/route/score-k8s/template/route-with-shared-gateway.md @@ -0,0 +1,23 @@ +--- +title: "route-with-shared-gateway" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "route" +provisionerType: "template" +flavor: "route" +excerpt: '' +description: 'Generates an HTTPRoute attached to a default Gateway in default Namespace.' +supportedParams: + - path + - host + - port +hasMore: false + +--- + +{{% resource-provisioner-content description="Generates an HTTPRoute attached to a default Gateway in default Namespace." type="route" supportedParams="path,host,port" %}} + +{{% example-file filename="10-shared-gateway-httproute.provisioners.yaml" dir="resource-provisioners/community/route/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/service/score-compose/template/static-service.md b/content/en/examples/resource-provisioners/community/service/score-compose/template/static-service.md new file mode 100644 index 00000000..8d772d5b --- /dev/null +++ b/content/en/examples/resource-provisioners/community/service/score-compose/template/static-service.md @@ -0,0 +1,21 @@ +--- +title: "static-service" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-compose" +resourceType: "service" +provisionerType: "template" +flavor: "static" +excerpt: '' +description: 'Outputs the name of the Workload dependency if it exists in the list of Workloads.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Outputs the name of the Workload dependency if it exists in the list of Workloads." type="service" expectedOutputs="name" %}} + +{{% example-file filename="10-service.provisioners.yaml" dir="resource-provisioners/community/service/score-compose" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/service/score-k8s/template/static-service-with-netpol.md b/content/en/examples/resource-provisioners/community/service/score-k8s/template/static-service-with-netpol.md new file mode 100644 index 00000000..af7a7dc4 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/service/score-k8s/template/static-service-with-netpol.md @@ -0,0 +1,21 @@ +--- +title: "static-service-with-netpol" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "service" +provisionerType: "template" +flavor: "static" +excerpt: '' +description: 'Outputs the name of the Workload dependency if it exists in the list of Workloads, and generate NetworkPolicies between them.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Outputs the name of the Workload dependency if it exists in the list of Workloads, and generate NetworkPolicies between them." type="service" expectedOutputs="name" %}} + +{{% example-file filename="10-service-with-netpol.provisioners.yaml" dir="resource-provisioners/community/service/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/community/service/score-k8s/template/static-service.md b/content/en/examples/resource-provisioners/community/service/score-k8s/template/static-service.md new file mode 100644 index 00000000..3e193209 --- /dev/null +++ b/content/en/examples/resource-provisioners/community/service/score-k8s/template/static-service.md @@ -0,0 +1,21 @@ +--- +title: "static-service" +draft: false +mermaid: true +type: examples +source: "community" +implementation: "score-k8s" +resourceType: "service" +provisionerType: "template" +flavor: "static" +excerpt: '' +description: 'Outputs the name of the Workload dependency if it exists in the list of Workloads.' +expectedOutputs: + - name +hasMore: false + +--- + +{{% resource-provisioner-content description="Outputs the name of the Workload dependency if it exists in the list of Workloads." type="service" expectedOutputs="name" %}} + +{{% example-file filename="10-service.provisioners.yaml" dir="resource-provisioners/community/service/score-k8s" githubUrl="https://github.com/score-spec/community-provisioners/blob/main" %}} diff --git a/content/en/examples/resource-provisioners/default/dns/score-compose/template/dns.md b/content/en/examples/resource-provisioners/default/dns/score-compose/template/dns.md new file mode 100644 index 00000000..c92e643a --- /dev/null +++ b/content/en/examples/resource-provisioners/default/dns/score-compose/template/dns.md @@ -0,0 +1,23 @@ +--- +title: "dns" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "dns" +provisionerType: "template" +flavor: "dns" +excerpt: 'The default dns provisioner just outputs localhost as the hostname every time. This is because without actual control of a dns resolver we can't do any accurate routing on any other name. This can be replaced by a new provisioner in the future.' +description: 'Outputs a *.localhost domain as the hostname.' +expectedOutputs: + - host +hasMore: true + +--- + +The default dns provisioner just outputs localhost as the hostname every time. This is because without actual control of a dns resolver we can't do any accurate routing on any other name. This can be replaced by a new provisioner in the future. + +{{% resource-provisioner-content description="Outputs a *.localhost domain as the hostname." type="dns" expectedOutputs="host" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/dns/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/dns/score-k8s/template/dns.md b/content/en/examples/resource-provisioners/default/dns/score-k8s/template/dns.md new file mode 100644 index 00000000..6553b5e0 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/dns/score-k8s/template/dns.md @@ -0,0 +1,23 @@ +--- +title: "dns" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "dns" +provisionerType: "template" +flavor: "dns" +excerpt: 'The default dns provisioner just outputs a random localhost domain because we don't know whether external-dns is available. You should replace this with your own dns name generation that matches your external-dns controller.' +description: 'Outputs a *.localhost domain as the hostname.' +expectedOutputs: + - host +hasMore: true + +--- + +The default dns provisioner just outputs a random localhost domain because we don't know whether external-dns is available. You should replace this with your own dns name generation that matches your external-dns controller. + +{{% resource-provisioner-content description="Outputs a *.localhost domain as the hostname." type="dns" expectedOutputs="host" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/dns/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/elasticsearch/score-compose/template/elasticsearch.md b/content/en/examples/resource-provisioners/default/elasticsearch/score-compose/template/elasticsearch.md new file mode 100644 index 00000000..cd10364f --- /dev/null +++ b/content/en/examples/resource-provisioners/default/elasticsearch/score-compose/template/elasticsearch.md @@ -0,0 +1,26 @@ +--- +title: "elasticsearch" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "elasticsearch" +provisionerType: "template" +flavor: "elasticsearch" +excerpt: 'The default elasticsearch provisioner adds a elasticsearch instance.' +description: 'Provisions a dedicated Elastic Search instance.' +expectedOutputs: + - host + - port + - username + - password +hasMore: false + +--- + +The default elasticsearch provisioner adds a elasticsearch instance. + +{{% resource-provisioner-content description="Provisions a dedicated Elastic Search instance." type="elasticsearch" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/elasticsearch/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/example-provisioner/score-k8s/cmd/example-provisioner.md b/content/en/examples/resource-provisioners/default/example-provisioner/score-k8s/cmd/example-provisioner.md new file mode 100644 index 00000000..01c1e08f --- /dev/null +++ b/content/en/examples/resource-provisioners/default/example-provisioner/score-k8s/cmd/example-provisioner.md @@ -0,0 +1,25 @@ +--- +title: "example-provisioner" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "example-provisioner-resource" +provisionerType: "cmd" +flavor: "example-provisioner" +excerpt: 'The 'cmd' scheme has a "host" + path component that indicates the path to the binary to execute. If the host starts with "." it is interpreted as a relative path, if it starts with "~" it resolves to the home directory.' +description: 'Example provisioner that runs a bash script.' +expectedOutputs: + - key + - secret +tool: example +hasMore: true + +--- + +The 'cmd' scheme has a "host" + path component that indicates the path to the binary to execute. If the host starts with "." it is interpreted as a relative path, if it starts with "~" it resolves to the home directory. + +{{% resource-provisioner-content description="Example provisioner that runs a bash script." type="example-provisioner-resource" expectedOutputs="key,secret" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/example-provisioner/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/kafka-topic/score-compose/template/kafka-topic.md b/content/en/examples/resource-provisioners/default/kafka-topic/score-compose/template/kafka-topic.md new file mode 100644 index 00000000..fae52985 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/kafka-topic/score-compose/template/kafka-topic.md @@ -0,0 +1,24 @@ +--- +title: "kafka-topic" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "kafka-topic" +provisionerType: "template" +flavor: "kafka" +excerpt: '' +description: 'Provisions a dedicated Kafka topic on a shared Kafka broker.' +expectedOutputs: + - host + - port + - name + - num_partitions +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated Kafka topic on a shared Kafka broker." type="kafka-topic" expectedOutputs="host,port,name,num_partitions" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/kafka-topic/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/mongo/score-k8s/template/mongo.md b/content/en/examples/resource-provisioners/default/mongo/score-k8s/template/mongo.md new file mode 100644 index 00000000..eb504e5b --- /dev/null +++ b/content/en/examples/resource-provisioners/default/mongo/score-k8s/template/mongo.md @@ -0,0 +1,25 @@ +--- +title: "mongo" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "mongodb" +provisionerType: "template" +flavor: "mongo" +excerpt: '' +description: 'Provisions a dedicated MongoDB database.' +expectedOutputs: + - host + - port + - username + - password + - connection +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated MongoDB database." type="mongodb" expectedOutputs="host,port,username,password,connection" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/mongo/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/mongodb/score-compose/template/mongodb.md b/content/en/examples/resource-provisioners/default/mongodb/score-compose/template/mongodb.md new file mode 100644 index 00000000..a0019e17 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/mongodb/score-compose/template/mongodb.md @@ -0,0 +1,27 @@ +--- +title: "mongodb" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "mongodb" +provisionerType: "template" +flavor: "mongodb" +excerpt: 'The default mongodb provisioner adds a mongodb service to the project which returns a host, port, username, and password, and connection string.' +description: 'Provisions a dedicated MongoDB database.' +expectedOutputs: + - host + - port + - username + - password + - connection +hasMore: false + +--- + +The default mongodb provisioner adds a mongodb service to the project which returns a host, port, username, and password, and connection string. + +{{% resource-provisioner-content description="Provisions a dedicated MongoDB database." type="mongodb" expectedOutputs="host,port,username,password,connection" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/mongodb/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/mssql/score-compose/template/mssql.md b/content/en/examples/resource-provisioners/default/mssql/score-compose/template/mssql.md new file mode 100644 index 00000000..bb693829 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/mssql/score-compose/template/mssql.md @@ -0,0 +1,26 @@ +--- +title: "mssql" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "mssql" +provisionerType: "template" +flavor: "mssql" +excerpt: '' +description: 'Provisions a dedicated database on a shared MS SQL server instance.' +expectedOutputs: + - server + - port + - connection + - database + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated database on a shared MS SQL server instance." type="mssql" expectedOutputs="server,port,connection,database,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/mssql/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/mssql/score-k8s/template/mssql.md b/content/en/examples/resource-provisioners/default/mssql/score-k8s/template/mssql.md new file mode 100644 index 00000000..80c854bf --- /dev/null +++ b/content/en/examples/resource-provisioners/default/mssql/score-k8s/template/mssql.md @@ -0,0 +1,26 @@ +--- +title: "mssql" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "mssql" +provisionerType: "template" +flavor: "mssql" +excerpt: '' +description: 'Provisions a dedicated database on a shared MS SQL server instance.' +expectedOutputs: + - server + - port + - connection + - database + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated database on a shared MS SQL server instance." type="mssql" expectedOutputs="server,port,connection,database,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/mssql/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/mysql/score-compose/template/mysql.md b/content/en/examples/resource-provisioners/default/mysql/score-compose/template/mysql.md new file mode 100644 index 00000000..dc6ceaba --- /dev/null +++ b/content/en/examples/resource-provisioners/default/mysql/score-compose/template/mysql.md @@ -0,0 +1,28 @@ +--- +title: "mysql" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "mysql" +provisionerType: "template" +flavor: "mysql" +excerpt: 'The default mysql provisioner adds a mysql instance and then ensures that the required databases are created on startup.' +description: 'Provisions a dedicated MySQL database on a shared instance.' +expectedOutputs: + - host + - port + - name + - database + - username + - password +hasMore: false + +--- + +The default mysql provisioner adds a mysql instance and then ensures that the required databases are created on startup. + +{{% resource-provisioner-content description="Provisions a dedicated MySQL database on a shared instance." type="mysql" expectedOutputs="host,port,name,database,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/mysql/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/mysql/score-k8s/template/mysql.md b/content/en/examples/resource-provisioners/default/mysql/score-k8s/template/mysql.md new file mode 100644 index 00000000..21cd6ed4 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/mysql/score-k8s/template/mysql.md @@ -0,0 +1,26 @@ +--- +title: "mysql" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "mysql" +provisionerType: "template" +flavor: "mysql" +excerpt: '' +description: 'Provisions a dedicated MySQL database on a shared instance.' +expectedOutputs: + - host + - port + - name + - database + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated MySQL database on a shared instance." type="mysql" expectedOutputs="host,port,name,database,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/mysql/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/postgres-instance/score-compose/template/postgres-instance.md b/content/en/examples/resource-provisioners/default/postgres-instance/score-compose/template/postgres-instance.md new file mode 100644 index 00000000..b25d2b90 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/postgres-instance/score-compose/template/postgres-instance.md @@ -0,0 +1,24 @@ +--- +title: "postgres-instance" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "postgres-instance" +provisionerType: "template" +flavor: "postgres" +excerpt: '' +description: 'Provisions a dedicated PostgreSQL instance.' +expectedOutputs: + - host + - port + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated PostgreSQL instance." type="postgres-instance" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/postgres-instance/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/postgres-instance/score-k8s/template/postgres-instance.md b/content/en/examples/resource-provisioners/default/postgres-instance/score-k8s/template/postgres-instance.md new file mode 100644 index 00000000..2d023f17 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/postgres-instance/score-k8s/template/postgres-instance.md @@ -0,0 +1,24 @@ +--- +title: "postgres-instance" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "postgres-instance" +provisionerType: "template" +flavor: "postgres" +excerpt: '' +description: 'Provisions a dedicated PostgreSQL instance.' +expectedOutputs: + - host + - port + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated PostgreSQL instance." type="postgres-instance" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/postgres-instance/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/postgres/score-compose/template/postgres.md b/content/en/examples/resource-provisioners/default/postgres/score-compose/template/postgres.md new file mode 100644 index 00000000..1c47e142 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/postgres/score-compose/template/postgres.md @@ -0,0 +1,28 @@ +--- +title: "postgres" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "postgres" +provisionerType: "template" +flavor: "postgres" +excerpt: 'The default postgres provisioner adds a postgres instance and then ensures that the required databases are created on startup.' +description: 'Provisions a dedicated database on a shared PostgreSQL instance.' +expectedOutputs: + - host + - port + - name + - database + - username + - password +hasMore: false + +--- + +The default postgres provisioner adds a postgres instance and then ensures that the required databases are created on startup. + +{{% resource-provisioner-content description="Provisions a dedicated database on a shared PostgreSQL instance." type="postgres" expectedOutputs="host,port,name,database,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/postgres/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/postgres/score-k8s/template/postgres.md b/content/en/examples/resource-provisioners/default/postgres/score-k8s/template/postgres.md new file mode 100644 index 00000000..35c7af26 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/postgres/score-k8s/template/postgres.md @@ -0,0 +1,26 @@ +--- +title: "postgres" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "postgres" +provisionerType: "template" +flavor: "postgres" +excerpt: '' +description: 'Provisions a dedicated database on a shared PostgreSQL instance.' +expectedOutputs: + - host + - port + - name + - database + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated database on a shared PostgreSQL instance." type="postgres" expectedOutputs="host,port,name,database,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/postgres/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/rabbitmq/score-compose/template/rabbitmq.md b/content/en/examples/resource-provisioners/default/rabbitmq/score-compose/template/rabbitmq.md new file mode 100644 index 00000000..fb7a3861 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/rabbitmq/score-compose/template/rabbitmq.md @@ -0,0 +1,27 @@ +--- +title: "rabbitmq" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "amqp" +provisionerType: "template" +flavor: "rabbitmq" +excerpt: 'The default AMQP provisioner provides a simple rabbitmq instance with default configuration and plugins.' +description: 'Provisions a dedicated RabbitMQ vhost on a shared instance.' +expectedOutputs: + - host + - port + - vhost + - username + - password +hasMore: false + +--- + +The default AMQP provisioner provides a simple rabbitmq instance with default configuration and plugins. + +{{% resource-provisioner-content description="Provisions a dedicated RabbitMQ vhost on a shared instance." type="amqp" expectedOutputs="host,port,vhost,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/rabbitmq/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/rabbitmq/score-k8s/template/rabbitmq.md b/content/en/examples/resource-provisioners/default/rabbitmq/score-k8s/template/rabbitmq.md new file mode 100644 index 00000000..b25e5bee --- /dev/null +++ b/content/en/examples/resource-provisioners/default/rabbitmq/score-k8s/template/rabbitmq.md @@ -0,0 +1,25 @@ +--- +title: "rabbitmq" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "amqp" +provisionerType: "template" +flavor: "rabbitmq" +excerpt: '' +description: 'Provisions a dedicated RabbitMQ vhost on a shared instance.' +expectedOutputs: + - host + - port + - vhost + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated RabbitMQ vhost on a shared instance." type="amqp" expectedOutputs="host,port,vhost,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/rabbitmq/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/redis/score-compose/template/redis.md b/content/en/examples/resource-provisioners/default/redis/score-compose/template/redis.md new file mode 100644 index 00000000..ba302e58 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/redis/score-compose/template/redis.md @@ -0,0 +1,26 @@ +--- +title: "redis" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "redis" +provisionerType: "template" +flavor: "redis" +excerpt: 'The default redis provisioner adds a redis service to the project which returns a host, port, username, and password.' +description: 'Provisions a dedicated Redis instance.' +expectedOutputs: + - host + - port + - username + - password +hasMore: false + +--- + +The default redis provisioner adds a redis service to the project which returns a host, port, username, and password. + +{{% resource-provisioner-content description="Provisions a dedicated Redis instance." type="redis" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/redis/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/redis/score-k8s/template/redis.md b/content/en/examples/resource-provisioners/default/redis/score-k8s/template/redis.md new file mode 100644 index 00000000..7f682cec --- /dev/null +++ b/content/en/examples/resource-provisioners/default/redis/score-k8s/template/redis.md @@ -0,0 +1,24 @@ +--- +title: "redis" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "redis" +provisionerType: "template" +flavor: "redis" +excerpt: '' +description: 'Provisions a dedicated Redis instance.' +expectedOutputs: + - host + - port + - username + - password +hasMore: false + +--- + +{{% resource-provisioner-content description="Provisions a dedicated Redis instance." type="redis" expectedOutputs="host,port,username,password" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/redis/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/route/score-compose/template/route.md b/content/en/examples/resource-provisioners/default/route/score-compose/template/route.md new file mode 100644 index 00000000..296f9ee1 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/route/score-compose/template/route.md @@ -0,0 +1,25 @@ +--- +title: "route" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "route" +provisionerType: "template" +flavor: "route" +excerpt: 'The default route provisioner sets up an nginx service with an HTTP service that can route on our prefix paths. It assumes the hostnames and routes provided have no overlaps. Weird behavior may happen if there are overlaps.' +description: 'Provisions a ingress route on a shared Nginx instance.' +supportedParams: + - host + - port + - path +hasMore: false + +--- + +The default route provisioner sets up an nginx service with an HTTP service that can route on our prefix paths. It assumes the hostnames and routes provided have no overlaps. Weird behavior may happen if there are overlaps. + +{{% resource-provisioner-content description="Provisions a ingress route on a shared Nginx instance." type="route" supportedParams="host,port,path" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/route/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/route/score-k8s/template/route.md b/content/en/examples/resource-provisioners/default/route/score-k8s/template/route.md new file mode 100644 index 00000000..58422558 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/route/score-k8s/template/route.md @@ -0,0 +1,25 @@ +--- +title: "route" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "route" +provisionerType: "template" +flavor: "route" +excerpt: 'Routes could be implemented as either traditional ingress resources or using the newer gateway API. In this default provisioner we use the gateway API with some sensible defaults. But you may wish to replace this.' +description: 'Provisions an HTTPRoute on a shared Nginx instance.' +supportedParams: + - host + - port + - path +hasMore: false + +--- + +Routes could be implemented as either traditional ingress resources or using the newer gateway API. In this default provisioner we use the gateway API with some sensible defaults. But you may wish to replace this. + +{{% resource-provisioner-content description="Provisions an HTTPRoute on a shared Nginx instance." type="route" supportedParams="host,port,path" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/route/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/s3/score-compose/template/s3.md b/content/en/examples/resource-provisioners/default/s3/score-compose/template/s3.md new file mode 100644 index 00000000..caf7ea5b --- /dev/null +++ b/content/en/examples/resource-provisioners/default/s3/score-compose/template/s3.md @@ -0,0 +1,29 @@ +--- +title: "s3" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "s3" +provisionerType: "template" +flavor: "s3" +excerpt: 'This resource provides a minio based S3 bucket with AWS-style credentials. This provides some common and well known outputs that can be used with any generic AWS s3 client. If the provider has a publish port annotation, it can expose a management port on the local network for debugging and connectivity.' +description: 'Provisions a dedicated S3 bucket with AWS-style credentials on a shared MinIO instance.' +expectedOutputs: + - bucket + - access_key_id + - secret_key + - endpoint + - region + - aws_access_key_id + - aws_secret_key +hasMore: false + +--- + +This resource provides a minio based S3 bucket with AWS-style credentials. This provides some common and well known outputs that can be used with any generic AWS s3 client. If the provider has a publish port annotation, it can expose a management port on the local network for debugging and connectivity. + +{{% resource-provisioner-content description="Provisions a dedicated S3 bucket with AWS-style credentials on a shared MinIO instance." type="s3" expectedOutputs="bucket,access_key_id,secret_key,endpoint,region,aws_access_key_id,aws_secret_key" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/s3/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/s3/score-k8s/template/s3.md b/content/en/examples/resource-provisioners/default/s3/score-k8s/template/s3.md new file mode 100644 index 00000000..e0d9a977 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/s3/score-k8s/template/s3.md @@ -0,0 +1,29 @@ +--- +title: "s3" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "s3" +provisionerType: "template" +flavor: "s3" +excerpt: 'This resource provides an in-cluster minio based S3 bucket with AWS-style credentials. This provides some common and well known outputs that can be used with any generic AWS s3 client. The outputs of the provisioner are a stateful set, a service, a secret, and a job per bucket.' +description: 'Provisions a dedicated S3 bucket with AWS-style credentials on a shared MinIO instance.' +expectedOutputs: + - bucket + - access_key_id + - secret_key + - endpoint + - region + - aws_access_key_id + - aws_secret_key +hasMore: false + +--- + +This resource provides an in-cluster minio based S3 bucket with AWS-style credentials. This provides some common and well known outputs that can be used with any generic AWS s3 client. The outputs of the provisioner are a stateful set, a service, a secret, and a job per bucket. + +{{% resource-provisioner-content description="Provisions a dedicated S3 bucket with AWS-style credentials on a shared MinIO instance." type="s3" expectedOutputs="bucket,access_key_id,secret_key,endpoint,region,aws_access_key_id,aws_secret_key" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/s3/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/service-port/score-compose/template/service-port.md b/content/en/examples/resource-provisioners/default/service-port/score-compose/template/service-port.md new file mode 100644 index 00000000..6b881680 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/service-port/score-compose/template/service-port.md @@ -0,0 +1,27 @@ +--- +title: "service-port" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "service-port" +provisionerType: "template" +flavor: "service" +excerpt: 'The default provisioner for service resources, this expects a workload and port name and will return the hostname and port required to contact it. This will validate that the workload and port exist, but won't enforce a dependency relationship yet.' +description: 'Outputs a hostname and port for connecting to another workload.' +expectedOutputs: + - hostname + - port +supportedParams: + - workload + - port +hasMore: true + +--- + +The default provisioner for service resources, this expects a workload and port name and will return the hostname and port required to contact it. This will validate that the workload and port exist, but won't enforce a dependency relationship yet. + +{{% resource-provisioner-content description="Outputs a hostname and port for connecting to another workload." type="service-port" supportedParams="workload,port" expectedOutputs="hostname,port" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/service-port/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/service-port/score-k8s/template/service-port.md b/content/en/examples/resource-provisioners/default/service-port/score-k8s/template/service-port.md new file mode 100644 index 00000000..7bba91af --- /dev/null +++ b/content/en/examples/resource-provisioners/default/service-port/score-k8s/template/service-port.md @@ -0,0 +1,27 @@ +--- +title: "service-port" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "service-port" +provisionerType: "template" +flavor: "service" +excerpt: 'The default provisioner for service resources, this expects a workload and port name and will return the hostname and port required to contact it. This will validate that the workload and port exist, but won't enforce a dependency relationship yet.' +description: 'Outputs a hostname and port for connecting to another workload.' +expectedOutputs: + - hostname + - port +supportedParams: + - workload + - port +hasMore: true + +--- + +The default provisioner for service resources, this expects a workload and port name and will return the hostname and port required to contact it. This will validate that the workload and port exist, but won't enforce a dependency relationship yet. + +{{% resource-provisioner-content description="Outputs a hostname and port for connecting to another workload." type="service-port" supportedParams="workload,port" expectedOutputs="hostname,port" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/service-port/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/volume/score-compose/template/volume.md b/content/en/examples/resource-provisioners/default/volume/score-compose/template/volume.md new file mode 100644 index 00000000..2758aecd --- /dev/null +++ b/content/en/examples/resource-provisioners/default/volume/score-compose/template/volume.md @@ -0,0 +1,24 @@ +--- +title: "volume" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-compose" +resourceType: "volume" +provisionerType: "template" +flavor: "volume" +excerpt: 'The default volume provisioner provided by score-compose allows basic volume resources to be created in the resources system. The volume resource just creates an ephemeral Docker volume with a random string as the name, and source attribute that we can reference.' +description: 'Creates a persistent volume that can be mounted on a workload.' +expectedOutputs: + - source + - type +hasMore: false + +--- + +The default volume provisioner provided by score-compose allows basic volume resources to be created in the resources system. The volume resource just creates an ephemeral Docker volume with a random string as the name, and source attribute that we can reference. + +{{% resource-provisioner-content description="Creates a persistent volume that can be mounted on a workload." type="volume" expectedOutputs="source,type" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/volume/score-compose" githubUrl="https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml" %}} diff --git a/content/en/examples/resource-provisioners/default/volume/score-k8s/template/volume.md b/content/en/examples/resource-provisioners/default/volume/score-k8s/template/volume.md new file mode 100644 index 00000000..db59cb91 --- /dev/null +++ b/content/en/examples/resource-provisioners/default/volume/score-k8s/template/volume.md @@ -0,0 +1,23 @@ +--- +title: "volume" +draft: false +mermaid: true +type: examples +source: "default" +implementation: "score-k8s" +resourceType: "volume" +provisionerType: "template" +flavor: "volume" +excerpt: 'As an example we have a 'volume' type which returns an emptyDir volume. In production or for real applications you may want to replace this with a provisioner for a tmpfs, host path, or persistent volume and claims.' +description: 'Creates a persistent volume that can be mounted on a workload.' +expectedOutputs: + - source +hasMore: true + +--- + +As an example we have a 'volume' type which returns an emptyDir volume. In production or for real applications you may want to replace this with a provisioner for a tmpfs, host path, or persistent volume and claims. + +{{% resource-provisioner-content description="Creates a persistent volume that can be mounted on a workload." type="volume" expectedOutputs="source" %}} + +{{% example-file filename="provisioners.yaml" dir="resource-provisioners/default/volume/score-k8s" githubUrl="https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml" %}} diff --git a/data/examplesMeta.yml b/data/examplesMeta.yml index 9e26dfee..2118d472 100644 --- a/data/examplesMeta.yml +++ b/data/examplesMeta.yml @@ -1 +1,66 @@ -{} \ No newline at end of file +--- +resource-provisioners: + implementation: + - score-compose + - score-k8s + provisionerType: + - cmd + - template + resourceType: + - amqp + - dapr-pubsub + - dapr-state-store + - dapr-subscription + - dns + - elasticsearch + - environment + - example-provisioner-resource + - horizontal-pod-autoscaler + - kafka-topic + - mongodb + - mssql + - mysql + - postgres + - postgres-instance + - redis + - route + - s3 + - service + - service-port + - volume + flavor: + - dapr + - default + - dns + - dns-in-codespace + - dotenv + - elasticsearch + - empty + - example-provisioner + - foo + - helm-template-redis + - helm-upgrade-redis + - ingress + - kafka + - mongo + - mongodb + - mssql + - mysql + - postgres + - rabbitmq + - redis + - route + - s3 + - service + - static + - volume + tool: + - dns + - dotenv + - example + - helm + source: + - community + - default +--- + diff --git a/gen/examples-site/examples-category-content/resource-provisioners.md b/gen/examples-site/examples-category-content/resource-provisioners.md new file mode 100644 index 00000000..83a16719 --- /dev/null +++ b/gen/examples-site/examples-category-content/resource-provisioners.md @@ -0,0 +1 @@ +The examples below illustrate how to use resources provisioners for each Score implementation. diff --git a/gen/examples-site/file-utils.js b/gen/examples-site/file-utils.js index c6ab0f39..ccf41ff0 100644 --- a/gen/examples-site/file-utils.js +++ b/gen/examples-site/file-utils.js @@ -36,9 +36,32 @@ function traverseDirectory(dir, callback) { }); } +/** + * Check if a folder is valid (is a directory and not ignored) + * @param {string} folderPath - Path to the folder + * @returns {boolean} - True if folder is valid + */ +const isValidFolder = (folderPath) => { + const folderName = folderPath.split("/").pop(); + return isDirectory(folderPath) && !shouldIgnoreFolder(folderName); +}; + +/** + * Check if a directory contains subdirectories + * @param {string} dirPath - Path to the directory + * @returns {boolean} - True if directory contains subdirectories + */ +const hasSubdirectories = (dirPath) => { + return fs + .readdirSync(dirPath) + .some((file) => isDirectory(`${dirPath}/${file}`)); +}; + module.exports = { isDirectory, mkdirIfNotExistsSync, traverseDirectory, shouldIgnoreFolder, + isValidFolder, + hasSubdirectories, }; diff --git a/gen/examples-site/frontmatter-provisioners.js b/gen/examples-site/frontmatter-provisioners.js new file mode 100644 index 00000000..2c417ac6 --- /dev/null +++ b/gen/examples-site/frontmatter-provisioners.js @@ -0,0 +1,211 @@ +const fs = require("fs"); +const path = require("path"); +const matter = require("gray-matter"); +const { getGitHubUrl } = require("./github-utils"); +const { + getMetadataFromReadme, + removeNonExternalLinks, + getExcerpt, + addAliasesToMetadata, + saveMetadata, +} = require("./metadata-utils"); +const { isDirectory } = require("./file-utils"); + +const sourceFolder = process.argv[2]; +const DEFAULT_PROVISIONER_URL_SCORE_K8S = + "https://github.com/score-spec/score-k8s/blob/main/internal/provisioners/default/zz-default.provisioners.yaml"; +const DEFAULT_PROVISIONER_URL_SCORE_COMPOSE = + "https://github.com/score-spec/score-compose/blob/main/internal/command/default.provisioners.yaml"; + +/** + * Converts an array to YAML array format. + * @param {Array} array - The array to convert. + * @returns {string} The YAML array format string. + */ +const convertToYamlArray = (array) => { + if (!array || !Array.isArray(array) || array.length === 0) return ""; + return array.map((item) => ` - ${item}`).join("\n"); +}; + +const generateFrontmatterContent = (config) => { + return `--- +title: "${config.title}" +draft: false +mermaid: true +type: examples +source: "${config.source}" +implementation: "${config.implementation}" +resourceType: "${config.resourceType}" +provisionerType: "${config.provisionerType}" +flavor: "${config.flavor}" +excerpt: '${config.excerpt}' +description: '${config.description}' +${ + config.expectedOutputs ? `expectedOutputs: \n${config.expectedOutputs}\n` : "" +}${ + config.supportedParams + ? `supportedParams: \n${config.supportedParams}\n` + : "" + }${config.tool ? `tool: ${config.tool}\n` : ""}hasMore: ${ + config.excerpt !== config.content.trim() ? "true" : "false" + } +${config.metadata} +--- + +${config.content}`; +}; + +/** + * Generates content for an example file using the example-file shortcode. + * @param {string} file - The filename of the example file. + * @param {string} dir - The directory containing the file. + * @param {string} githubUrl - The GitHub URL for the file. + * @returns {string} The generated example file content. + */ +const generateExampleFileContent = (file, dir, githubUrl) => { + return `{{% example-file filename="${file}" dir="${dir}" githubUrl="${githubUrl}" %}}`; +}; + +/** + * Processes the README.md file in the given path and extracts metadata, content, and excerpt. + * @param {string} dirPath - The path to the directory containing the README.md file. + * @returns {Object} An object containing excerpt, content, and metadata. + */ +const processReadme = (dirPath) => { + let excerpt = ""; + let content = ""; + let metadata = ""; + if (fs.existsSync(`${sourceFolder}/${dirPath}/README.md`)) { + const readme = fs.readFileSync( + `${sourceFolder}/${dirPath}/README.md`, + "utf8" + ); + const { metadata: readmeMetadata, parsedFrontmatter } = + getMetadataFromReadme(readme, dirPath.split("/")[0]); + metadata = readmeMetadata; + content = removeNonExternalLinks(parsedFrontmatter.content); + excerpt = getExcerpt(content); + metadata = addAliasesToMetadata(dirPath, metadata); + } + return { excerpt, content, metadata }; +}; + +/** + * Writes content to a file in the examples directory. + * @param {string} filePath - The path where the file should be written. + * @param {string} content - The content to write to the file. + */ +const writeContentToFile = (filePath, content) => { + const fullPath = `./content/en/examples/${filePath}.md`; + const dirPath = path.dirname(fullPath); + fs.mkdirSync(dirPath, { recursive: true }); + fs.writeFileSync(fullPath, content); +}; + +const generateResourceProvisionerContent = (parsedYaml) => { + return `{{% resource-provisioner-content description="${ + parsedYaml.description + }" type="${parsedYaml.type}" ${ + parsedYaml.supported_params + ? `supportedParams="${parsedYaml.supported_params}"` + : "" + } ${ + parsedYaml.expected_outputs + ? `expectedOutputs="${parsedYaml.expected_outputs}"` + : "" + } %}}`; +}; + +/** + * Determines the GitHub URL for a provisioner based on type and implementation. + * @param {string} dirPath - The path to the provisioner directory. + * @param {Object} options - Options object containing source. + * @param {string} implementation - The implementation type (e.g., "score-k8s", "score-compose"). + * @returns {string} The GitHub URL for the provisioner. + */ +const getProvisionerGitHubUrl = (dirPath, options, implementation) => { + let githubUrl = getGitHubUrl(dirPath); + if (options.source === "default") { + if (implementation === "score-k8s") { + githubUrl = DEFAULT_PROVISIONER_URL_SCORE_K8S; + } else if (implementation === "score-compose") { + githubUrl = DEFAULT_PROVISIONER_URL_SCORE_COMPOSE; + } + } + return githubUrl; +}; + +/** + * Builds frontmatter and content for an example page. + * @param {string} dirPath - The path to the example directory. + * @param {Object} [options] - Additional options for the frontmatter. + */ +const buildResourceProvisionerFiles = ( + dirPath, + options = { source: "default" } +) => { + // Get and parse the YAML file inside the path + const yamlFiles = fs + .readdirSync(`${sourceFolder}/${dirPath}`) + .filter((file) => file.endsWith(".yaml") || file.endsWith(".yml")); + for (const yamlFile of yamlFiles) { + const yamlFilePath = `${sourceFolder}/${dirPath}/${yamlFile}`; + const yamlContent = fs.readFileSync(yamlFilePath, "utf8"); + const parsedYaml = matter(`---\n${yamlContent}\n---`).data[0]; + const implementation = dirPath.split("/").pop(); + const readmeConfig = processReadme(options.readmeLocation || dirPath); + const dir = options.fileLocation || dirPath.replace(/\.md$/, ""); + const githubUrl = getProvisionerGitHubUrl(dirPath, options, implementation); + const uriParts = parsedYaml.uri.split("/"); + let title = uriParts[uriParts.length - 1]; + let flavor = title.split("-")[0]; + let tool; + // Handle URIs with # (e.g., cmd://bash#example-provisioner) + if (title.includes("#")) { + title = title.split("#")[1]; + flavor = title; + tool = title.split("-")[0]; + } + const provisionerType = parsedYaml.uri.split("://")[0]; + + saveMetadata( + { + data: { + source: options.source, + implementation, + tool, + provisionerType, + resourceType: parsedYaml.type, + flavor, + }, + }, + "resource-provisioners" + ); + + const frontmatterContent = generateFrontmatterContent({ + ...readmeConfig, + title, + provisionerType, + source: options.source, + resourceType: parsedYaml.type, + description: parsedYaml.description, + implementation, + flavor, + expectedOutputs: convertToYamlArray(parsedYaml.expected_outputs), + supportedParams: convertToYamlArray(parsedYaml.supported_params), + tool, + }); + + writeContentToFile( + `${dirPath}/${provisionerType}/${title}`, + `${frontmatterContent} + +${generateResourceProvisionerContent(parsedYaml)} + +${generateExampleFileContent(yamlFile, dir, githubUrl)}\n +` + ); + } +}; + +module.exports = { buildResourceProvisionerFiles }; diff --git a/gen/examples-site/gen-example-pages.js b/gen/examples-site/gen-example-pages.js index 45338522..895136b9 100644 --- a/gen/examples-site/gen-example-pages.js +++ b/gen/examples-site/gen-example-pages.js @@ -1,13 +1,18 @@ const fs = require("fs"); const { buildFrontmatter } = require("./frontmatter"); +const { buildResourceProvisionerFiles } = require("./frontmatter-provisioners"); const { parseConfig } = require("./config-parser"); const { - isDirectory, mkdirIfNotExistsSync, - shouldIgnoreFolder, + isValidFolder, + hasSubdirectories, } = require("./file-utils"); const { beautify } = require("./content-utils"); +// Constants +const CONTENT_OUTPUT_BASE = "./content/en/examples"; +const CATEGORY_CONTENT_PATH = "./gen/examples-site/examples-category-content"; +const README_FILE = "README.md"; const sourceFolder = process.argv[2]; //Get the folders inside sourceFolder: @@ -21,78 +26,43 @@ const config = parseConfig("./config.toml"); const blacklist = config.exampleLibraryBlacklistedFolders; // Remove blacklisted folders from the categoryFolders array: -categoryFolders.forEach((folder, index) => { - if (blacklist.includes(folder)) { - categoryFolders.splice(index, 1); - } -}); +const filteredCategoryFolders = categoryFolders.filter( + (folder) => !blacklist.includes(folder) +); //For each folder, make a folder in ./content/examples: -for (const categoryFolder of categoryFolders) { - mkdirIfNotExistsSync(`./content/en/examples/${categoryFolder}`); +for (const categoryFolder of filteredCategoryFolders) { + mkdirIfNotExistsSync(`${CONTENT_OUTPUT_BASE}/${categoryFolder}`); + createCategoryIndex(categoryFolder); - //Write an _index.md file in each folder. - //Get content from examples-site category content files - const categoryIndexContent = fs.readFileSync( - `./gen/examples-site/examples-category-content/${categoryFolder}.md`, - "utf8" - ); - fs.writeFileSync( - `./content/en/examples/${categoryFolder}/_index.md`, - `--- -title: "${beautify(categoryFolder)}" -draft: false -type: examples ---- -${categoryIndexContent} - ----` - ); - - //Get the folders inside each category: + //Process the folders inside each category: const folders = fs.readdirSync(`${sourceFolder}/${categoryFolder}`); - - //For each folder, check if this is the last nesting level: for (const folder of folders) { - //Discard readme and other files: - if ( - !isDirectory(`${sourceFolder}/${categoryFolder}/${folder}`) || - shouldIgnoreFolder(folder) - ) { + if (!isValidFolder(`${sourceFolder}/${categoryFolder}/${folder}`)) { continue; } - const isLastNestingLevel = !fs - .readdirSync(`${sourceFolder}/${categoryFolder}/${folder}`) - .some((file) => - isDirectory(`${sourceFolder}/${categoryFolder}/${folder}/${file}`) - ); + const isLastNestingLevel = !hasSubdirectories( + `${sourceFolder}/${categoryFolder}/${folder}` + ); if (!isLastNestingLevel) { const subfolders = fs.readdirSync( `${sourceFolder}/${categoryFolder}/${folder}` ); - const folderPath = `./content/en/examples/${categoryFolder}/${folder}`; + const folderPath = `${CONTENT_OUTPUT_BASE}/${categoryFolder}/${folder}`; for (const subfolder of subfolders) { mkdirIfNotExistsSync(folderPath); - //Discard readme and other files, and dot folders: if ( - !isDirectory( + !isValidFolder( `${sourceFolder}/${categoryFolder}/${folder}/${subfolder}` - ) || - shouldIgnoreFolder(subfolder) + ) ) { continue; } - const isLastNestingLevel = !fs - .readdirSync( - `${sourceFolder}/${categoryFolder}/${folder}/${subfolder}` - ) - .some((file) => - isDirectory( - `${sourceFolder}/${categoryFolder}/${folder}/${subfolder}/${file}` - ) - ); + const isLastNestingLevel = !hasSubdirectories( + `${sourceFolder}/${categoryFolder}/${folder}/${subfolder}` + ); if (!isLastNestingLevel) { const subsubfolders = fs.readdirSync( @@ -101,22 +71,27 @@ ${categoryIndexContent} const subfolderPath = `${folderPath}/${subfolder}`; for (const subsubfolder of subsubfolders) { mkdirIfNotExistsSync(subfolderPath); - //Discard readme and other files, and dot folders: if ( - !isDirectory( + !isValidFolder( `${sourceFolder}/${categoryFolder}/${folder}/${subfolder}/${subsubfolder}` - ) || - shouldIgnoreFolder(subsubfolder) + ) ) { continue; } - buildFrontmatter( - subsubfolder, - `${categoryFolder}/${folder}/${subfolder}/${subsubfolder}`, - subfolder, - folder - ); + if (categoryFolder === "resource-provisioners") { + buildResourceProvisionerFiles( + `${categoryFolder}/${folder}/${subfolder}/${subsubfolder}`, + { source: folder } + ); + } else { + buildFrontmatter( + subsubfolder, + `${categoryFolder}/${folder}/${subfolder}/${subsubfolder}`, + subfolder, + folder + ); + } } } else { buildFrontmatter( @@ -128,45 +103,80 @@ ${categoryIndexContent} } } else { if (config.exampleLibraryOnePagePerFileFolders.includes(categoryFolder)) { - const path = `${sourceFolder}/${categoryFolder}/${folder}`; - const files = fs.readdirSync(path); - // Create output directory structure - mkdirIfNotExistsSync( - `./content/en/examples/${categoryFolder}/${folder}` - ); - for (const file of files) { - if (file === "README.md") continue; - const fileWithoutExtension = file.replace(/\.[^/.]+$/, ""); - mkdirIfNotExistsSync(`${path}/${fileWithoutExtension}`); - fs.renameSync( - `${path}/${file}`, - `${path}/${fileWithoutExtension}/${file}` - ); - buildFrontmatter( - fileWithoutExtension, - `${categoryFolder}/${folder}/${fileWithoutExtension}`, - folder, - "", - { - fileLocation: `${categoryFolder}/${folder}`, - readmeLocation: `${categoryFolder}/${folder}`, - shouldBeautifyParent: false, - } - ); - } - // Move files back to their original location - for (const file of files) { - if (file === "README.md") continue; - const fileWithoutExtension = file.replace(/\.[^/.]+$/, ""); - fs.renameSync( - `${path}/${fileWithoutExtension}/${file}`, - `${path}/${file}` - ); - fs.rmdirSync(`${path}/${fileWithoutExtension}`); - } + processOnePagePerFileFolder(categoryFolder, folder); } else { buildFrontmatter(folder, `${categoryFolder}/${folder}`); } } } } + +/** + * Process folders that need one page per file + * Temporarily moves files into subdirectories for processing, then moves them back + */ +function processOnePagePerFileFolder(categoryFolder, folder) { + const sourcePath = `${sourceFolder}/${categoryFolder}/${folder}`; + const files = fs.readdirSync(sourcePath); + + // Create output directory structure + mkdirIfNotExistsSync(`${CONTENT_OUTPUT_BASE}/${categoryFolder}/${folder}`); + + // Temporarily move files into subdirectories for processing + for (const file of files) { + if (file === README_FILE) continue; + + const fileWithoutExtension = file.replace(/\.[^/.]+$/, ""); + const tempDir = `${sourcePath}/${fileWithoutExtension}`; + + mkdirIfNotExistsSync(tempDir); + fs.renameSync(`${sourcePath}/${file}`, `${tempDir}/${file}`); + + buildFrontmatter( + fileWithoutExtension, + `${categoryFolder}/${folder}/${fileWithoutExtension}`, + folder, + "", + { + fileLocation: `${categoryFolder}/${folder}`, + readmeLocation: `${categoryFolder}/${folder}`, + shouldBeautifyParent: false, + } + ); + } + + // Move files back to their original location + for (const file of files) { + if (file === README_FILE) continue; + + const fileWithoutExtension = file.replace(/\.[^/.]+$/, ""); + const tempDir = `${sourcePath}/${fileWithoutExtension}`; + + fs.renameSync(`${tempDir}/${file}`, `${sourcePath}/${file}`); + fs.rmdirSync(tempDir); + } +} + +/** + * Create the _index.md file for a category folder + */ +function createCategoryIndex(categoryFolder) { + const categoryIndexContent = fs.readFileSync( + `${CATEGORY_CONTENT_PATH}/${categoryFolder}.md`, + "utf8" + ); + + const indexContent = `--- +title: "${beautify(categoryFolder)}" +draft: false +type: examples +--- +${categoryIndexContent} + +---`; + + fs.writeFileSync( + `${CONTENT_OUTPUT_BASE}/${categoryFolder}/_index.md`, + indexContent + ); +} diff --git a/gen/examples-site/metadata-utils.js b/gen/examples-site/metadata-utils.js index 202327aa..2db41f42 100644 --- a/gen/examples-site/metadata-utils.js +++ b/gen/examples-site/metadata-utils.js @@ -1,13 +1,13 @@ -const fs = require('fs'); -const matter = require('gray-matter'); +const fs = require("fs"); +const matter = require("gray-matter"); -const savedMetadataPath = './data/examplesMeta.yml'; -const aliasesPath = './data/examplesAliases.yaml'; -const aliasesFile = fs.readFileSync(aliasesPath, 'utf8'); +const savedMetadataPath = "./data/examplesMeta.yml"; +const aliasesPath = "./data/examplesAliases.yaml"; +const aliasesFile = fs.readFileSync(aliasesPath, "utf8"); const aliases = matter(`---\n${aliasesFile}\n---`).data; function saveMetadata(parsedFrontmatter, exampleType) { - const savedMetadataFile = fs.readFileSync(savedMetadataPath, 'utf8'); + const savedMetadataFile = fs.readFileSync(savedMetadataPath, "utf8"); const savedMetadata = matter(savedMetadataFile).data; // If the example type is not in the saved metadata, add it to the saved metadata: @@ -16,25 +16,25 @@ function saveMetadata(parsedFrontmatter, exampleType) { } for (const key in parsedFrontmatter.data) { - // If the key is not in the saved metadata, add it to the saved metadata: + if (!parsedFrontmatter.data[key]) { + continue; + } + // If the key is not in the saved metadata, add it to the saved metadata as an array: if (!savedMetadata[exampleType][key]) { - savedMetadata[exampleType][key] = parsedFrontmatter.data[key]; - if (Array.isArray(savedMetadata[key])) { - savedMetadata[exampleType][key] = savedMetadata[key].sort(); - } + savedMetadata[exampleType][key] = [parsedFrontmatter.data[key]].sort(); } else { // If the key is in the saved metadata and is an array, merge the arrays: if (Array.isArray(savedMetadata[exampleType][key])) { savedMetadata[exampleType][key] = Array.from( new Set([ ...savedMetadata[exampleType][key], - ...parsedFrontmatter.data[key], + parsedFrontmatter.data[key], ]) ).sort(); } } } - fs.writeFileSync(savedMetadataPath, matter.stringify('', savedMetadata)); + fs.writeFileSync(savedMetadataPath, matter.stringify("", savedMetadata)); } function saveRDTypeToMetadata(type, title) { @@ -43,12 +43,12 @@ function saveRDTypeToMetadata(type, title) { function getMetadataFromReadme(readmeContent, exampleType) { const parsedFrontmatter = matter(readmeContent); - let metadata = ''; + let metadata = ""; if (Object.keys(parsedFrontmatter.data).length > 0) { saveMetadata(parsedFrontmatter, exampleType); - const yamlMetadata = matter.stringify('', parsedFrontmatter.data); - metadata = yamlMetadata.replace(/---/g, '').trim(); + const yamlMetadata = matter.stringify("", parsedFrontmatter.data); + metadata = yamlMetadata.replace(/---/g, "").trim(); } return { metadata, parsedFrontmatter }; @@ -57,10 +57,10 @@ function getMetadataFromReadme(readmeContent, exampleType) { function addAliasesToMetadata(path, existingMetadata) { if (aliases[`/examples/${path}`]) { const aliasString = matter - .stringify('', { + .stringify("", { aliases: aliases[`/examples/${path}`], }) - .replace(/---/g, '') + .replace(/---/g, "") .trim(); return existingMetadata ? `${existingMetadata}\n${aliasString}` @@ -72,7 +72,7 @@ function addAliasesToMetadata(path, existingMetadata) { function removeNonExternalLinks(content) { const regex = /\[([^\]]+)\]\(([^)]+)\)/g; return content.replace(regex, (match, p1, p2) => { - if (p2.startsWith('http://') || p2.startsWith('https://')) { + if (p2.startsWith("http://") || p2.startsWith("https://")) { return match; } return `\`${p1}\``; @@ -81,10 +81,13 @@ function removeNonExternalLinks(content) { function getExcerpt(content) { const textWithoutHeadings = content - .replace(/^\s*#+.*$/gm, '') - .replace(/---/g, ''); - return textWithoutHeadings.trim().split('\n\n')[0].split('\r\n\r\n')[0]. - replace(/'/g, '''); + .replace(/^\s*#+.*$/gm, "") + .replace(/---/g, ""); + return textWithoutHeadings + .trim() + .split("\n\n")[0] + .split("\r\n\r\n")[0] + .replace(/'/g, "'"); } module.exports = { diff --git a/gen/examples-site/transform-default-resource-provisioners.js b/gen/examples-site/transform-default-resource-provisioners.js new file mode 100644 index 00000000..4792eaa6 --- /dev/null +++ b/gen/examples-site/transform-default-resource-provisioners.js @@ -0,0 +1,180 @@ +const fs = require("fs"); +const path = require("path"); +const matter = require("gray-matter"); + +const scoreComposeDir = path.join( + __dirname, + "../external-content/resource-provisioners/default/score-compose" +); +const scoreK8sDir = path.join( + __dirname, + "../external-content/resource-provisioners/default/score-k8s" +); + +const directories = [scoreComposeDir, scoreK8sDir]; + +function cleanDirectory(dirPath) { + if (!fs.existsSync(dirPath)) { + return; + } + + const items = fs.readdirSync(dirPath); + items.forEach((item) => { + const fullPath = path.join(dirPath, item); + const stats = fs.lstatSync(fullPath); + + if (stats.isDirectory()) { + fs.rmSync(fullPath, { recursive: true, force: true }); + } else if (stats.isFile()) { + const ext = path.extname(item).toLowerCase(); + if (ext !== ".yaml" && ext !== ".yml") { + fs.unlinkSync(fullPath); + } + } + }); +} + +function extractProvisionerSection(content, uri) { + const lines = content.split("\n"); + let startIdx = -1; + let endIdx = -1; + let commentStartIdx = -1; + + // Find the line with the URI + for (let i = 0; i < lines.length; i++) { + if (lines[i].includes(`- uri: ${uri}`)) { + startIdx = i; + + // Go backwards to find the start of comments + commentStartIdx = i - 1; + while ( + commentStartIdx >= 0 && + lines[commentStartIdx].trim().startsWith("#") + ) { + commentStartIdx--; + } + commentStartIdx++; // Move to the first comment line + + // Find the end of this provisioner (next "- uri:" or end of file) + for (let j = i + 1; j < lines.length; j++) { + if (lines[j].match(/^- uri:/)) { + // Found the next provisioner, now go backwards to find where its comments start + let commentStart = j - 1; + while ( + commentStart > i && + lines[commentStart].trim().startsWith("#") + ) { + commentStart--; + } + endIdx = commentStart + 1; + break; + } + } + if (endIdx === -1) { + endIdx = lines.length; + } + break; + } + } + + if (startIdx === -1) { + return { yamlContent: null, comments: null }; + } + + // Extract the provisioner YAML (without leading comments) + const yamlLines = lines.slice(startIdx, endIdx); + const yamlContent = yamlLines.join("\n").trim(); + + // Extract just the comments for README + const commentLines = lines.slice(commentStartIdx, startIdx); + const comments = commentLines + .map((line) => line.trim().replace(/^#\s?/, "")) + .join(" ") + .trim(); + + return { yamlContent, comments }; +} + +function splitProvisionersFile(sourceFile, baseDir, implementation) { + const content = fs.readFileSync(sourceFile, "utf8"); + + // Parse YAML to get the list of provisioners and their URIs + const parsed = matter(`---\n${content}\n---`); + const provisioners = parsed.data; + + if (!Array.isArray(provisioners)) { + console.error(`Expected array in ${sourceFile}`); + return; + } + + provisioners.forEach((provisioner, index) => { + if (!provisioner.uri) { + console.warn( + `Provisioner at index ${index} in ${sourceFile} has no uri, skipping` + ); + return; + } + + // Extract folder name from URI + const uriParts = provisioner.uri.split("/"); + let folderName = uriParts[uriParts.length - 1]; + if (!folderName) { + console.warn( + `Provisioner at index ${index} in ${sourceFile} has invalid uri, skipping` + ); + return; + } + // Handle URIs with # (e.g., cmd://bash#example-provisioner) + if (folderName.includes("#")) { + folderName = folderName.split("#")[1]; + } + + const targetDir = path.join(baseDir, "..", folderName, implementation); + + // Create directory if it doesn't exist + fs.mkdirSync(targetDir, { recursive: true }); + + const targetFile = path.join(targetDir, "provisioners.yaml"); + + // Extract the raw YAML section from the original file + const { yamlContent, comments } = extractProvisionerSection( + content, + provisioner.uri + ); + + if (yamlContent) { + fs.writeFileSync(targetFile, yamlContent, "utf8"); + + // Create README.md with comments + if (comments) { + const readmeFile = path.join(targetDir, "README.md"); + fs.writeFileSync(readmeFile, comments, "utf8"); + } + } else { + console.warn(`Could not extract YAML for ${provisioner.uri}`); + } + }); +} + +// Clean directories first +directories.forEach((dir) => { + cleanDirectory(dir); +}); + +// Split the provisioners files +const scoreComposeYaml = path.join( + scoreComposeDir, + "default.provisioners.yaml" +); +const scoreK8sYaml = path.join(scoreK8sDir, "zz-default.provisioners.yaml"); + +if (fs.existsSync(scoreComposeYaml)) { + splitProvisionersFile(scoreComposeYaml, scoreComposeDir, "score-compose"); +} + +if (fs.existsSync(scoreK8sYaml)) { + splitProvisionersFile(scoreK8sYaml, scoreK8sDir, "score-k8s"); +} + +fs.rmSync(scoreComposeDir, { recursive: true, force: true }); +fs.rmSync(scoreK8sDir, { recursive: true, force: true }); diff --git a/gen/external-content/patch-templates/score-k8s/delete-default-manifests.tpl b/gen/external-content/patch-templates/score-k8s/delete-default-manifests.tpl index f2bf4220..a71c42a1 100644 --- a/gen/external-content/patch-templates/score-k8s/delete-default-manifests.tpl +++ b/gen/external-content/patch-templates/score-k8s/delete-default-manifests.tpl @@ -8,4 +8,3 @@ - op: delete path: {{ $i }} {{ end }} -{{ end }} diff --git a/gen/external-content/resource-provisioners/community/.devcontainer/devcontainer.json b/gen/external-content/resource-provisioners/community/.devcontainer/devcontainer.json new file mode 100644 index 00000000..9ae3be89 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +{ + "name": "Score Dev Container", + "image": "mcr.microsoft.com/devcontainers/base:noble", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": true, + "version": "latest" + }, + "ghcr.io/devcontainers/features/kubectl-helm-minikube:1": { + "version": "latest", + "helm": "latest", + "minikube": "latest" + }, + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + "postCreateCommand": "bash .devcontainer/installMoreTools.sh", + "customizations": { + "vscode": { + "extensions": [ + "redhat.vscode-yaml" + ], + "settings": { + "yaml.schemas": { + "https://raw.githubusercontent.com/score-spec/spec/main/score-v1b1.json": "score.yaml" + } + } + } + } +} \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/.devcontainer/installMoreTools.sh b/gen/external-content/resource-provisioners/community/.devcontainer/installMoreTools.sh new file mode 100755 index 00000000..0c0d25e7 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/.devcontainer/installMoreTools.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +mkdir install-more-tools +cd install-more-tools + +SCORE_COMPOSE_VERSION=$(curl -sL https://api.github.com/repos/score-spec/score-compose/releases/latest | jq -r .tag_name) +wget https://github.com/score-spec/score-compose/releases/download/${SCORE_COMPOSE_VERSION}/score-compose_${SCORE_COMPOSE_VERSION}_linux_amd64.tar.gz +tar -xvf score-compose_${SCORE_COMPOSE_VERSION}_linux_amd64.tar.gz +chmod +x score-compose +sudo mv score-compose /usr/local/bin + +SCORE_K8S_VERSION=$(curl -sL https://api.github.com/repos/score-spec/score-k8s/releases/latest | jq -r .tag_name) +wget https://github.com/score-spec/score-k8s/releases/download/${SCORE_K8S_VERSION}/score-k8s_${SCORE_K8S_VERSION}_linux_amd64.tar.gz +tar -xvf score-k8s_${SCORE_K8S_VERSION}_linux_amd64.tar.gz +chmod +x score-k8s +sudo mv score-k8s /usr/local/bin + +KIND_VERSION=$(curl -sL https://api.github.com/repos/kubernetes-sigs/kind/releases/latest | jq -r .tag_name) +curl -Lo ./kind https://kind.sigs.k8s.io/dl/${KIND_VERSION}/kind-linux-amd64 +chmod +x ./kind +sudo mv ./kind /usr/local/bin/kind + +sudo wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /usr/bin/yq +sudo chmod +x /usr/bin/yq + +cd .. +rm -rf install-more-tools \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/.github/workflows/ci.yaml b/gen/external-content/resource-provisioners/community/.github/workflows/ci.yaml new file mode 100644 index 00000000..b8975828 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/.github/workflows/ci.yaml @@ -0,0 +1,30 @@ +name: ci +permissions: + contents: read +on: + pull_request: + push: + branches: + - main +jobs: + test-provisioners: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: setup-kind-cluster + run: | + .scripts/setup-kind-cluster.sh + - uses: score-spec/setup-score@v2 + with: + file: score-compose + version: latest + token: ${{ secrets.GITHUB_TOKEN }} + - uses: score-spec/setup-score@v2 + with: + file: score-k8s + version: latest + token: ${{ secrets.GITHUB_TOKEN }} + - name: test-provisioners + run: | + .scripts/test-provisioners.sh \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/.github/workflows/release.yaml b/gen/external-content/resource-provisioners/community/.github/workflows/release.yaml new file mode 100644 index 00000000..b36052e9 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/.github/workflows/release.yaml @@ -0,0 +1,35 @@ +name: release +permissions: write-all +on: + push: + tags: + - 'v*' +jobs: + release: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: oras-project/setup-oras@v1 + - name: oras login + run: | + echo ${{ secrets.GITHUB_TOKEN }} | oras login ghcr.io -u ${{ github.actor }} --password-stdin + - name: oras push score-compose + run: | + mkdir compose + provisioners=$(find -name *.provisioners.yaml | grep /score-compose) + for provisioner in $provisioners; + do + cp $provisioner compose/ + done + cd compose/ + oras push ghcr.io/${{ github.repository_owner }}/score-compose-community-provisioners:${{ github.ref_name }},latest $(ls .) + - name: oras push score-k8s + run: | + mkdir k8s + provisioners=$(find -name *.provisioners.yaml | grep /score-k8s) + for provisioner in $provisioners; + do + cp $provisioner k8s/ + done + cd k8s/ + oras push ghcr.io/${{ github.repository_owner }}/score-k8s-community-provisioners:${{ github.ref_name }},latest $(ls .) diff --git a/gen/external-content/resource-provisioners/community/.gitignore b/gen/external-content/resource-provisioners/community/.gitignore new file mode 100644 index 00000000..a53b9f57 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/.gitignore @@ -0,0 +1,5 @@ +compose.yaml +manifests.yaml +.score-compose/ +.score-k8s/ +score.yaml diff --git a/gen/external-content/resource-provisioners/community/.scripts/setup-kind-cluster.sh b/gen/external-content/resource-provisioners/community/.scripts/setup-kind-cluster.sh new file mode 100755 index 00000000..5df5af9d --- /dev/null +++ b/gen/external-content/resource-provisioners/community/.scripts/setup-kind-cluster.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -o errexit + +cat <&2 + HELM_TEMPLATE_OUTPUT_TEMP_FILE=$(mktemp) + helm template ${SERVICE} bitnami/redis --set replica.replicaCount=1 > ${HELM_TEMPLATE_OUTPUT_TEMP_FILE} + MANIFESTS_IN_JSON=$(yq ea '[.]' -o json -I=0 ${HELM_TEMPLATE_OUTPUT_TEMP_FILE}) + OUTPUTS='{"resource_outputs":{"host":"%s-master", "port":"6379", "username":"", "password":"🔐💬%s_redis-password💬🔐"},"manifests":%s}' + printf "$OUTPUTS" "$SERVICE" "$SERVICE" "$MANIFESTS_IN_JSON" + expected_outputs: + - host + - port + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/redis/score-k8s/10-redis-helm-upgrade.provisioners.yaml b/gen/external-content/resource-provisioners/community/redis/score-k8s/10-redis-helm-upgrade.provisioners.yaml new file mode 100644 index 00000000..185ecc33 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/redis/score-k8s/10-redis-helm-upgrade.provisioners.yaml @@ -0,0 +1,22 @@ +- uri: cmd://bash#helm-upgrade-redis + type: redis + description: Deploys the bitnami/redis Helm chart in an existing cluster. + args: + - -c + - | + STDIN=$(cat) + SERVICE=$(echo $STDIN | yq eval -p json '.resource_id' | yq '. |= sub("\.", "-")') + NAMESPACE=$(echo $STDIN | yq eval -p json '.namespace') + if [ -z "$NAMESPACE" ]; then + NAMESPACE="default" + fi + set -eu -o pipefail + helm repo add bitnami https://charts.bitnami.com/bitnami >&2 + helm upgrade -i ${SERVICE} bitnami/redis --set replica.replicaCount=1 -n ${NAMESPACE} --wait >&2 + OUTPUTS='{"resource_outputs":{"host":"%s-master", "port":"6379", "username":"", "password":"🔐💬%s_redis-password💬🔐"},"manifests":[]}' + printf "$OUTPUTS" "$SERVICE" "$SERVICE" + expected_outputs: + - host + - port + - username + - password diff --git a/gen/external-content/resource-provisioners/community/redis/score-k8s/README.md b/gen/external-content/resource-provisioners/community/redis/score-k8s/README.md new file mode 100644 index 00000000..d1ad8775 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/redis/score-k8s/README.md @@ -0,0 +1,12 @@ +## For `10-redis-helm-template.provisioners.yaml` + +Prerequisites: +- Have `helm` installed locally, this provisioner renders the manifests from the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have `yq` installed locally. + +## For `10-redis-helm-upgrade.provisioners.yaml` + +Prerequisites: +- Have `helm` installed locally, this provisioner installs the [Bitnami's Redis Helm chart](https://bitnami.com/stack/redis/helm). +- Have access to a cluster where the Helm chart will be installed. + - If you don't have one, you can deploy a `Kind` cluster locally by running this script: `.scripts/setup-kind-cluster.sh`. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/redis/score.yaml b/gen/external-content/resource-provisioners/community/redis/score.yaml new file mode 100755 index 00000000..c6c7b29d --- /dev/null +++ b/gen/external-content/resource-provisioners/community/redis/score.yaml @@ -0,0 +1,16 @@ +apiVersion: score.dev/v1b1 +metadata: + name: my-workload +containers: + my-container: + image: busybox + command: ["/bin/sh"] + args: ["-c", "while true; do echo $REDIS_HOST; sleep 5; done"] + variables: + REDIS_HOST: ${resources.my-redis.host} + REDIS_PORT: ${resources.my-redis.port} + REDIS_USERNAME: ${resources.my-redis.username} + REDIS_PASSWORD: ${resources.my-redis.password} +resources: + my-redis: + type: redis \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/route/score-k8s/10-ingress-route.provisioners.yaml b/gen/external-content/resource-provisioners/community/route/score-k8s/10-ingress-route.provisioners.yaml new file mode 100644 index 00000000..142eed61 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/route/score-k8s/10-ingress-route.provisioners.yaml @@ -0,0 +1,37 @@ +- uri: template://community-provisioners/ingress-route + type: route + description: Provisions an Ingress route on a shared nginx instance. + supported_params: + - path + - host + - port + init: | + {{ if not (regexMatch "^/|(/([^/]+))+$" .Params.path) }}{{ fail "params.path start with a / but cannot end with /" }}{{ end }} + {{ if not (regexMatch "^[a-z0-9_.-]{1,253}$" .Params.host) }}{{ fail (cat "params.host must be a valid hostname but was" .Params.host) }}{{ end }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ if not $ports }}{{ fail "no service ports exist" }}{{ end }} + {{ $port := index $ports (print .Params.port) }} + {{ if not $port.TargetPort }}{{ fail "params.port is not a named service port" }}{{ end }} + state: | + routeName: route-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + manifests: | + - apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: {{ .State.routeName }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + spec: + ingressClassName: nginx + rules: + - host: {{ .Params.host | quote }} + http: + paths: + - path: {{ .Params.path | quote }} + pathType: Prefix + backend: + service: + name: {{ (index .WorkloadServices .SourceWorkload).ServiceName }} + port: + number: {{ .Params.port }} diff --git a/gen/external-content/resource-provisioners/community/route/score-k8s/10-ingress-with-netpol-route.provisioners.yaml b/gen/external-content/resource-provisioners/community/route/score-k8s/10-ingress-with-netpol-route.provisioners.yaml new file mode 100644 index 00000000..835755d4 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/route/score-k8s/10-ingress-with-netpol-route.provisioners.yaml @@ -0,0 +1,62 @@ +- uri: template://community-provisioners/ingress-with-net-pol-route + type: route + description: Provisions an Ingress route on a shared nginx instance, and a NetworkPolicy between them. + supported_params: + - path + - host + - port + init: | + {{ if not (regexMatch "^/|(/([^/]+))+$" .Params.path) }}{{ fail "params.path start with a / but cannot end with /" }}{{ end }} + {{ if not (regexMatch "^[a-z0-9_.-]{1,253}$" .Params.host) }}{{ fail (cat "params.host must be a valid hostname but was" .Params.host) }}{{ end }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ if not $ports }}{{ fail "no service ports exist" }}{{ end }} + {{ $port := index $ports (print .Params.port) }} + {{ if not $port.TargetPort }}{{ fail "params.port is not a named service port" }}{{ end }} + targetPort: {{ $port.TargetPort }} + state: | + routeName: route-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + manifests: | + - apiVersion: networking.k8s.io/v1 + kind: Ingress + metadata: + name: {{ .State.routeName }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + spec: + ingressClassName: nginx + rules: + - host: {{ .Params.host | quote }} + http: + paths: + - path: {{ .Params.path | quote }} + pathType: Prefix + backend: + service: + name: {{ (index .WorkloadServices .SourceWorkload).ServiceName }} + port: + number: {{ .Params.port }} + - apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: ingress-to-{{ .SourceWorkload }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ .SourceWorkload }} + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: ingress-nginx + podSelector: + matchLabels: + app.kubernetes.io/name: ingress-nginx + ports: + - protocol: TCP + port: {{ .Init.targetPort }} diff --git a/gen/external-content/resource-provisioners/community/route/score-k8s/10-shared-gateway-httproute-with-netpol.provisioners.yaml b/gen/external-content/resource-provisioners/community/route/score-k8s/10-shared-gateway-httproute-with-netpol.provisioners.yaml new file mode 100644 index 00000000..fd4095d1 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/route/score-k8s/10-shared-gateway-httproute-with-netpol.provisioners.yaml @@ -0,0 +1,67 @@ +- uri: template://community-provisioners/route-with-shared-gateway-with-netpol + type: route + description: Generates an HTTPRoute attached to a default Gateway in default Namespace, and a NetworkPolicy between them. + supported_params: + - path + - host + - port + init: | + {{ if not (regexMatch "^/|(/([^/]+))+$" .Params.path) }}{{ fail "params.path start with a / but cannot end with /" }}{{ end }} + {{ if not (regexMatch "^[a-z0-9_.-]{1,253}$" .Params.host) }}{{ fail (cat "params.host must be a valid hostname but was" .Params.host) }}{{ end }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ if not $ports }}{{ fail "no service ports exist" }}{{ end }} + {{ $port := index $ports (print .Params.port) }} + {{ if not $port.TargetPort }}{{ fail "params.port is not a named service port" }}{{ end }} + targetPort: {{ $port.TargetPort }} + gatewayNamespace: default + gatewayName: default + state: | + routeName: route-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + manifests: | + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: {{ .State.routeName }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.routeName }} + app.kubernetes.io/instance: {{ .State.routeName }} + spec: + parentRefs: + - name: {{ .Init.gatewayName }} + namespace: {{ .Init.gatewayNamespace }} + hostnames: + - {{ .Params.host | quote }} + rules: + - matches: + - path: + type: PathPrefix + value: {{ .Params.path | quote }} + backendRefs: + - name: {{ (index .WorkloadServices .SourceWorkload).ServiceName }} + port: {{ .Params.port }} + - apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: gateway-to-{{ .SourceWorkload }} + spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ .SourceWorkload }} + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: nginx-gateway + podSelector: + matchLabels: + app.kubernetes.io/name: nginx-gateway-fabric + ports: + - protocol: TCP + port: {{ .Init.targetPort }} diff --git a/gen/external-content/resource-provisioners/community/route/score-k8s/10-shared-gateway-httproute.provisioners.yaml b/gen/external-content/resource-provisioners/community/route/score-k8s/10-shared-gateway-httproute.provisioners.yaml new file mode 100644 index 00000000..a88eed2e --- /dev/null +++ b/gen/external-content/resource-provisioners/community/route/score-k8s/10-shared-gateway-httproute.provisioners.yaml @@ -0,0 +1,48 @@ +- uri: template://community-provisioners/route-with-shared-gateway + type: route + description: Generates an HTTPRoute attached to a default Gateway in default Namespace. + supported_params: + - path + - host + - port + init: | + {{ if not (regexMatch "^/|(/([^/]+))+$" .Params.path) }}{{ fail "params.path start with a / but cannot end with /" }}{{ end }} + {{ if not (regexMatch "^[a-z0-9_.-]{1,253}$" .Params.host) }}{{ fail (cat "params.host must be a valid hostname but was" .Params.host) }}{{ end }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ if not $ports }}{{ fail "no service ports exist" }}{{ end }} + {{ $port := index $ports (print .Params.port) }} + {{ if not $port.TargetPort }}{{ fail "params.port is not a named service port" }}{{ end }} + gatewayNamespace: default + gatewayName: default + state: | + routeName: route-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + manifests: | + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: {{ .State.routeName }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.routeName }} + app.kubernetes.io/instance: {{ .State.routeName }} + spec: + parentRefs: + - name: {{ .Init.gatewayName }} + namespace: {{ .Init.gatewayNamespace }} + hostnames: + - {{ .Params.host | quote }} + rules: + - matches: + - path: + type: PathPrefix + value: {{ .Params.path | quote }} + backendRefs: + - name: {{ (index .WorkloadServices .SourceWorkload).ServiceName }} + port: {{ .Params.port }} diff --git a/gen/external-content/resource-provisioners/community/route/score.yaml b/gen/external-content/resource-provisioners/community/route/score.yaml new file mode 100644 index 00000000..be4a8f08 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/route/score.yaml @@ -0,0 +1,22 @@ +apiVersion: score.dev/v1b1 +metadata: + name: my-workload +containers: + my-container: + image: ghcr.io/stefanprodan/podinfo:latest + variables: + PODINFO_UI_MESSAGE: "Hello, ${resources.dns.host}!" +resources: + dns: + type: dns + route: + type: route + params: + host: ${resources.dns.host} + path: / + port: 8080 +service: + ports: + tcp: + port: 8080 + targetPort: 9898 \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/score-compose.md b/gen/external-content/resource-provisioners/community/score-compose.md new file mode 100644 index 00000000..b92c5e22 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/score-compose.md @@ -0,0 +1,43 @@ +Initialize your local workspace, by importing a specific community provisioner: + +```bash +score-commpose init --provisioners REPLACE-ME-WITH-ACTUAL-PROVISIONER-FILE-URL.yaml +``` + +_Note: you need to replace `REPLACE-ME-WITH-ACTUAL-PROVISIONER-FILE-URL.yaml` by the actual provisioner file you want to use and import. More information [here](https://docs.score.dev/docs/score-implementation/score-compose/resources-provisioners/#install-provisioner-files)._ + +Get the provisioners definition: + +```bash +score-compose provisioners list +``` + +Generate the platform specific manifests: + +```bash +score-commpose generate score.yaml +``` + +See the resource outputs: + +```bash +score-commpose resources list +``` + +You can run the following command on each resource listed with the previous command to get their `outputs`: + +```bash +score-commpose resources get-outputs +``` + +Deploy the generated manifests: + +```bash +docker compose up -d +``` + +See the running containers: + +```bash +docker ps +``` diff --git a/gen/external-content/resource-provisioners/community/score-k8s.md b/gen/external-content/resource-provisioners/community/score-k8s.md new file mode 100644 index 00000000..c9e876c8 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/score-k8s.md @@ -0,0 +1,43 @@ +Initialize your local workspace, by importing a specific community provisioner: + +```bash +score-k8s init --provisioners REPLACE-ME-WITH-ACTUAL-PROVISIONER-FILE-URL.yaml +``` + +_Note: you need to replace `REPLACE-ME-WITH-ACTUAL-PROVISIONER-FILE-URL.yaml` by the actual provisioner file you want to use and import. More information [here](https://docs.score.dev/docs/score-implementation/score-k8s/resources-provisioners/#install-provisioner-files)._ + +Get the provisioners definition: + +```bash +score-k8s provisioners list +``` + +Generate the platform specific manifests: + +```bash +score-k8s generate score.yaml +``` + +See the resource outputs: + +```bash +score-k8s resources list +``` + +You can run the following command on each resource listed with the previous command to get their `outputs`: + +```bash +score-k8s resources get-outputs +``` + +Deploy the generated manifests: + +```bash +kubectl apply -f manifests.yaml +``` + +See the running containers: + +```bash +kubectl get all +``` diff --git a/gen/external-content/resource-provisioners/community/service/score-backend.yaml b/gen/external-content/resource-provisioners/community/service/score-backend.yaml new file mode 100755 index 00000000..1779778e --- /dev/null +++ b/gen/external-content/resource-provisioners/community/service/score-backend.yaml @@ -0,0 +1,8 @@ +apiVersion: score.dev/v1b1 +metadata: + name: backend +containers: + my-container: + image: busybox + command: ["/bin/sh"] + args: ["-c", "while true; do echo Hello Backend; sleep 5; done"] \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/service/score-compose/10-service.provisioners.yaml b/gen/external-content/resource-provisioners/community/service/score-compose/10-service.provisioners.yaml new file mode 100644 index 00000000..6ac10b4b --- /dev/null +++ b/gen/external-content/resource-provisioners/community/service/score-compose/10-service.provisioners.yaml @@ -0,0 +1,11 @@ +- uri: template://community-provisioners/static-service + type: service + description: Outputs the name of the Workload dependency if it exists in the list of Workloads. + init: | + name: {{ splitList "." .Id | last }} + outputs: | + {{ $w := (index .WorkloadServices .Init.name) }} + {{ if or (not $w) (not $w.ServiceName) }}{{ fail "unknown workload" }}{{ end }} + name: {{ $w.ServiceName | quote }} + expected_outputs: + - name diff --git a/gen/external-content/resource-provisioners/community/service/score-frontend.yaml b/gen/external-content/resource-provisioners/community/service/score-frontend.yaml new file mode 100755 index 00000000..d60f0491 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/service/score-frontend.yaml @@ -0,0 +1,13 @@ +apiVersion: score.dev/v1b1 +metadata: + name: frontend +containers: + my-container: + image: busybox + command: ["/bin/sh"] + args: ["-c", "while true; do echo $BACKEND_SVC; sleep 5; done"] + variables: + BACKEND_SVC: http://${resources.backend.name} +resources: + backend: + type: service \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/community/service/score-k8s/10-service-with-netpol.provisioners.yaml b/gen/external-content/resource-provisioners/community/service/score-k8s/10-service-with-netpol.provisioners.yaml new file mode 100644 index 00000000..2a6151e1 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/service/score-k8s/10-service-with-netpol.provisioners.yaml @@ -0,0 +1,64 @@ +- uri: template://community-provisioners/static-service-with-netpol + type: service + description: Outputs the name of the Workload dependency if it exists in the list of Workloads, and generate NetworkPolicies between them. + init: | + name: {{ splitList "." .Id | last }} + outputs: | + {{ $w := (index .WorkloadServices .Init.name) }} + {{ if or (not $w) (not $w.ServiceName) }}{{ fail "unknown workload" }}{{ end }} + name: {{ .Init.name }} + expected_outputs: + - name + manifests: | + - apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: {{ .Init.name }}-from-{{ .SourceWorkload }}-ingress + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ .Init.name }} + policyTypes: + - Ingress + ingress: + - from: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ .SourceWorkload }} + namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .Namespace | default "default" }} + ports: + {{- range $k, $port := (index .WorkloadServices .Init.name).Ports }} + - protocol: TCP + port: {{ $port.TargetPort }} + {{- end }} + - apiVersion: networking.k8s.io/v1 + kind: NetworkPolicy + metadata: + name: {{ .SourceWorkload }}-to-{{ .Init.name }}-egress + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + spec: + podSelector: + matchLabels: + app.kubernetes.io/name: {{ .SourceWorkload }} + policyTypes: + - Egress + egress: + - to: + - podSelector: + matchLabels: + app.kubernetes.io/name: {{ .Init.name }} + namespaceSelector: + matchLabels: + kubernetes.io/metadata.name: {{ .Namespace | default "default" }} + ports: + {{- range $k, $port := (index .WorkloadServices .Init.name).Ports }} + - protocol: TCP + port: {{ $port.TargetPort }} + {{- end }} diff --git a/gen/external-content/resource-provisioners/community/service/score-k8s/10-service.provisioners.yaml b/gen/external-content/resource-provisioners/community/service/score-k8s/10-service.provisioners.yaml new file mode 100644 index 00000000..7202e173 --- /dev/null +++ b/gen/external-content/resource-provisioners/community/service/score-k8s/10-service.provisioners.yaml @@ -0,0 +1,11 @@ +- uri: template://community-provisioners/static-service + type: service + description: Outputs the name of the Workload dependency if it exists in the list of Workloads. + init: | + name: {{ splitList "." .Id | last }} + outputs: | + {{ $w := (index .WorkloadServices .Init.name) }} + {{ if or (not $w) (not $w.ServiceName) }}{{ fail "unknown workload" }}{{ end }} + name: {{ $w.ServiceName | quote }} + expected_outputs: + - name diff --git a/gen/external-content/resource-provisioners/default/dns/score-compose/README.md b/gen/external-content/resource-provisioners/default/dns/score-compose/README.md new file mode 100644 index 00000000..69db207b --- /dev/null +++ b/gen/external-content/resource-provisioners/default/dns/score-compose/README.md @@ -0,0 +1 @@ +The default dns provisioner just outputs localhost as the hostname every time. This is because without actual control of a dns resolver we can't do any accurate routing on any other name. This can be replaced by a new provisioner in the future. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/dns/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/dns/score-compose/provisioners.yaml new file mode 100644 index 00000000..96d1d7ab --- /dev/null +++ b/gen/external-content/resource-provisioners/default/dns/score-compose/provisioners.yaml @@ -0,0 +1,11 @@ +- uri: template://default-provisioners/dns + type: dns + description: Outputs a *.localhost domain as the hostname. + init: | + randomHostname: dns{{ randAlphaNum 6 | lower }}.localhost + state: | + instanceHostname: {{ dig "instanceHostname" .Init.randomHostname .State | quote }} + outputs: | + host: {{ .State.instanceHostname }} + expected_outputs: + - host \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/dns/score-k8s/README.md b/gen/external-content/resource-provisioners/default/dns/score-k8s/README.md new file mode 100644 index 00000000..3f5de71d --- /dev/null +++ b/gen/external-content/resource-provisioners/default/dns/score-k8s/README.md @@ -0,0 +1 @@ +The default dns provisioner just outputs a random localhost domain because we don't know whether external-dns is available. You should replace this with your own dns name generation that matches your external-dns controller. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/dns/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/dns/score-k8s/provisioners.yaml new file mode 100644 index 00000000..96d1d7ab --- /dev/null +++ b/gen/external-content/resource-provisioners/default/dns/score-k8s/provisioners.yaml @@ -0,0 +1,11 @@ +- uri: template://default-provisioners/dns + type: dns + description: Outputs a *.localhost domain as the hostname. + init: | + randomHostname: dns{{ randAlphaNum 6 | lower }}.localhost + state: | + instanceHostname: {{ dig "instanceHostname" .Init.randomHostname .State | quote }} + outputs: | + host: {{ .State.instanceHostname }} + expected_outputs: + - host \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/elasticsearch/score-compose/README.md b/gen/external-content/resource-provisioners/default/elasticsearch/score-compose/README.md new file mode 100644 index 00000000..c68b9aa9 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/elasticsearch/score-compose/README.md @@ -0,0 +1 @@ +The default elasticsearch provisioner adds a elasticsearch instance. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/elasticsearch/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/elasticsearch/score-compose/provisioners.yaml new file mode 100644 index 00000000..842d86a4 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/elasticsearch/score-compose/provisioners.yaml @@ -0,0 +1,136 @@ +- uri: template://default-provisioners/elasticsearch + # By default, match all elasticsearch types regardless of class and id. + # If you want to override this, create another provisioner definition with a higher priority. + type: elasticsearch + description: Provisions a dedicated Elastic Search instance. + # Init template has the random service name and password if needed later + init: | + serviceName: elasticsearch + randomPassword: {{ randAlphaNum 16 | quote }} + clusterName: cluster-ecs-{{ randAlphaNum 6 }} + username: elastic + sk: default-provisioners-elasticsearch-instance + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "9200" .Metadata | quote }} + license: {{ dig "annotations" "compose.score.dev/license" "basic" .Metadata | quote }} + stackVersion: {{ dig "annotations" "compose.score.dev/stack-version" "8.14.0" .Metadata | quote }} + esMemLimit: {{ dig "annotations" "compose.score.dev/es-mem-limit" "1073741824" .Metadata | quote }} + # The state for each elasticsearch resource is a unique host, port, and credentials + state: | + clusterName: {{ dig "clusterName" .Init.clusterName .State | quote }} + username: {{ dig "username" .Init.username .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + host: {{ dig "host" .Init.serviceName .State | quote }} + outputs: | + host: {{ .State.host }} + port: {{ .Init.publishPort }} + username: {{ .State.username | quote }} + password: {{ .State.password | quote }} + # Ensure the data volume exists + volumes: | + ecscerts: + driver: local + ecsdata: + driver: local + # Create 2 services, the first is the setup container which creates the certificates, the second is the elasticsearch itself + services: | + setup: + image: docker.elastic.co/elasticsearch/elasticsearch:{{ .Init.stackVersion }} + volumes: + - type: volume + source: ecscerts + target: /usr/share/elasticsearch/config/certs + user: "0" + command: + - "bash" + - "-c" + - | + if [ ! -f config/certs/ca.zip ]; then + echo "Creating CA"; + bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip; + unzip config/certs/ca.zip -d config/certs; + fi; + if [ ! -f config/certs/certs.zip ]; then + echo "Creating certs"; + echo -ne \ + "instances:\n"\ + " - name: {{ .State.host }}\n"\ + " dns:\n"\ + " - {{ .State.host }}\n"\ + " - localhost\n"\ + " ip:\n"\ + " - 127.0.0.1\n"\ + > config/certs/instances.yml; + bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key; + unzip config/certs/certs.zip -d config/certs; + fi; + echo "Setting file permissions" + chown -R root:root config/certs; + find . -type d -exec chmod 750 \{\} \;; + find . -type f -exec chmod 640 \{\} \;; + echo "Waiting for Elasticsearch availability"; + until curl -s --cacert config/certs/ca/ca.crt https://{{ .State.host }}:9200 | grep -q "missing authentication credentials"; do sleep 10; done; + echo "All done!"; + healthcheck: + test: ["CMD-SHELL", "[ -f config/certs/{{ .State.host }}/{{ .State.host }}.crt ]"] + interval: 1s + timeout: 5s + retries: 120 + {{ .State.host }}: + depends_on: + setup: + condition: service_healthy + image: docker.elastic.co/elasticsearch/elasticsearch:{{ .Init.stackVersion }} + labels: + co.elastic.logs/module: elasticsearch + volumes: + - type: volume + source: ecscerts + target: /usr/share/elasticsearch/config/certs + - type: volume + source: ecsdata + target: /usr/share/elasticsearch/data + ports: + - target: 9200 + published: {{ .Init.publishPort }} + environment: + - node.name={{ .State.host }} + - cluster.name={{ .State.clusterName }} + - discovery.type=single-node + - bootstrap.memory_lock=true + - ELASTIC_PASSWORD={{ .State.password }} + - xpack.security.enabled=true + - xpack.security.http.ssl.enabled=true + - xpack.security.http.ssl.key=certs/{{ .State.host }}/{{ .State.host }}.key + - xpack.security.http.ssl.certificate=certs/{{ .State.host }}/{{ .State.host }}.crt + - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt + - xpack.security.transport.ssl.enabled=true + - xpack.security.transport.ssl.key=certs/{{ .State.host }}/{{ .State.host }}.key + - xpack.security.transport.ssl.certificate=certs/{{ .State.host }}/{{ .State.host }}.crt + - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt + - xpack.security.transport.ssl.verification_mode=certificate + - xpack.license.self_generated.type={{ .Init.license }} + mem_limit: {{ .Init.esMemLimit }} + ulimits: + memlock: + soft: -1 + hard: -1 + healthcheck: + test: + [ + "CMD-SHELL", + "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'", + ] + interval: 10s + timeout: 10s + retries: 120 + info_logs: | + - "{{.Uid}}: To connect to elasticsearch:\n + download certificate from container per command like: \n + \tdocker cp [CONTAINER-NAME]:/usr/share/elasticsearch/config/certs/ca/ca.crt /tmp/ \n + and than check connection per culr like: \n + \tcurl --cacert /tmp/ca.crt -u {{ .State.username }}:{{ .State.password }} https://localhost:{{ .Init.publishPort }}" + expected_outputs: + - host + - port + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/example-provisioner/score-k8s/README.md b/gen/external-content/resource-provisioners/default/example-provisioner/score-k8s/README.md new file mode 100644 index 00000000..ee0f8a90 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/example-provisioner/score-k8s/README.md @@ -0,0 +1 @@ +The 'cmd' scheme has a "host" + path component that indicates the path to the binary to execute. If the host starts with "." it is interpreted as a relative path, if it starts with "~" it resolves to the home directory. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/example-provisioner/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/example-provisioner/score-k8s/provisioners.yaml new file mode 100644 index 00000000..f2b2283e --- /dev/null +++ b/gen/external-content/resource-provisioners/default/example-provisioner/score-k8s/provisioners.yaml @@ -0,0 +1,11 @@ +- uri: cmd://bash#example-provisioner + type: example-provisioner-resource + description: Example provisioner that runs a bash script. + class: default + id: specific + # (Optional) additional args that the binary gets run with + # If any of the args are '' it will be replaced with "provision" + args: ["-c", "echo '{\"resource_outputs\":{\"key\":\"value\",\"secret\":\"🔐💬mysecret_mykey💬🔐\"},\"manifests\":[]}'"] + expected_outputs: + - key + - secret \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/kafka-topic/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/kafka-topic/score-compose/provisioners.yaml new file mode 100644 index 00000000..08c3b6b1 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/kafka-topic/score-compose/provisioners.yaml @@ -0,0 +1,61 @@ +- uri: template://default-provisioners/kafka-topic + type: kafka-topic + description: Provisions a dedicated Kafka topic on a shared Kafka broker. + init: | + brokerPort: 9092 + ctrlPort: 9093 + state: | + topic: {{ dig "topic" (print "topic-" (randAlphaNum 6)) .State | quote }} + shared: | + shared_kafka_instance_name: {{ dig "shared_kafka_instance_name" (print "kafka-" (randAlphaNum 6)) .Shared | quote }} + services: | + {{ .Shared.shared_kafka_instance_name }}: + image: bitnami/kafka:latest + restart: always + environment: + KAFKA_CFG_NODE_ID: "0" + KAFKA_CFG_PROCESS_ROLES: controller,broker + KAFKA_CFG_LISTENERS: "PLAINTEXT://:{{ .Init.brokerPort }},CONTROLLER://:{{ .Init.ctrlPort }}" + KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP: "CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT" + KAFKA_CFG_CONTROLLER_QUORUM_VOTERS: "0@{{ .Shared.shared_kafka_instance_name }}:{{ .Init.ctrlPort }}" + KAFKA_CFG_CONTROLLER_LISTENER_NAMES: CONTROLLER + KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE: "false" + healthcheck: + test: ["CMD", "kafka-topics.sh", "--list", "--bootstrap-server=localhost:{{ .Init.brokerPort }}"] + interval: 2s + timeout: 2s + retries: 10 + {{ $publishPort := (dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | atoi) }} + {{ if ne $publishPort 0 }} + ports: + - target: {{ .Init.brokerPort }} + published: {{ $publishPort }} + {{ end }} + volumes: + - type: volume + source: {{ .Shared.shared_kafka_instance_name }}-data + target: /bitnami/kafka + {{ .State.topic }}-init: + image: bitnami/kafka:latest + entrypoint: ["/bin/sh"] + command: ["-c", "kafka-topics.sh --topic={{.State.topic}} --bootstrap-server=localhost:{{ .Init.brokerPort }} --describe || kafka-topics.sh --topic={{.State.topic}} --bootstrap-server=localhost:{{ .Init.brokerPort }} --create --partitions=3"] + network_mode: "service:{{ .Shared.shared_kafka_instance_name }}" + labels: + dev.score.compose.labels.is-init-container: "true" + depends_on: + {{ .Shared.shared_kafka_instance_name }}: + condition: service_healthy + restart: true + volumes: | + {{ .Shared.shared_kafka_instance_name }}-data: + driver: local + outputs: | + host: {{ .Shared.shared_kafka_instance_name }} + port: "{{ .Init.brokerPort }}" + name: {{ .State.topic }} + num_partitions: 3 + expected_outputs: + - host + - port + - name + - num_partitions \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mongo/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/mongo/score-k8s/provisioners.yaml new file mode 100644 index 00000000..ebf9eefa --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mongo/score-k8s/provisioners.yaml @@ -0,0 +1,156 @@ +- uri: template://default-provisioners/mongo + type: mongodb + description: Provisions a dedicated MongoDB database. + init: | + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: mongo-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.service }} + port: 27017 + connection: "mongodb://{{ .State.username }}:{{ .State.password }}@{{ .State.service }}:27017/" + username: {{ .State.username }} + password: {{ encodeSecretRef .State.service "MONGO_INITDB_ROOT_PASSWORD" }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + data: + MONGO_INITDB_ROOT_PASSWORD: {{ .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + replicas: 1 + serviceName: {{ .State.service }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + spec: + automountServiceAccountToken: false + containers: + - name: mongo-db + image: mirror.gcr.io/mongo:8 + ports: + - name: mongo + containerPort: 27017 + env: + - name: MONGO_INITDB_ROOT_USERNAME + value: {{ .State.username | quote }} + - name: MONGO_INITDB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .State.service }} + key: MONGO_INITDB_ROOT_PASSWORD + livenessProbe: + exec: + command: + - /bin/sh + - -c + - echo 'db.runCommand("ping").ok' | mongosh -u $$MONGO_INITDB_ROOT_USERNAME -p $$MONGO_INITDB_ROOT_PASSWORD + initialDelaySeconds: 30 + timeoutSeconds: 5 + periodSeconds: 20 + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + allowPrivilegeEscalation: false + privileged: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + volumeMounts: + - name: data + mountPath: /data/db + - name: tmp + mountPath: /tmp + securityContext: + runAsNonRoot: true + fsGroup: 1001 + seccompProfile: + type: RuntimeDefault + volumes: + - name: tmp + emptyDir: {} + volumeClaimTemplates: + - metadata: + name: data + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + ports: + - port: 27017 + targetPort: 27017 + expected_outputs: + - host + - port + - username + - password + - connection \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mongodb/score-compose/README.md b/gen/external-content/resource-provisioners/default/mongodb/score-compose/README.md new file mode 100644 index 00000000..acd2d512 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mongodb/score-compose/README.md @@ -0,0 +1 @@ +The default mongodb provisioner adds a mongodb service to the project which returns a host, port, username, and password, and connection string. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mongodb/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/mongodb/score-compose/provisioners.yaml new file mode 100644 index 00000000..9f38e3ad --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mongodb/score-compose/provisioners.yaml @@ -0,0 +1,53 @@ +- uri: template://default-provisioners/mongodb + type: mongodb + description: Provisions a dedicated MongoDB database. + init: | + port: 27017 + randomServiceName: mongo-{{ randAlphaNum 6 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + serviceName: {{ dig "serviceName" .Init.randomServiceName .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.serviceName }} + port: {{ .Init.port }} + username: {{ .State.username | quote }} + password: {{ .State.password | quote }} + connection: "mongodb://{{ .State.username }}:{{ .State.password }}@{{ .State.serviceName }}:{{ .Init.port }}/" + volumes: | + {{ .State.serviceName }}-data: + name: {{ .State.serviceName }}-data + driver: local + labels: + dev.score.compose.res.uid: {{ .Uid }} + services: | + {{ .State.serviceName }}: + labels: + dev.score.compose.res.uid: {{ .Uid }} + image: mirror.gcr.io/mongo:8 + restart: always + environment: + MONGO_INITDB_ROOT_USERNAME: {{ .State.username | quote }} + MONGO_INITDB_ROOT_PASSWORD: {{ .State.password | quote }} + healthcheck: + test: ["CMD-SHELL", "echo 'db.runCommand(\"ping\").ok' | mongosh -u $$MONGO_INITDB_ROOT_USERNAME -p $$MONGO_INITDB_ROOT_PASSWORD"] + interval: 2s + timeout: 5s + retries: 15 + start_period: 10s + volumes: + - type: volume + source: {{ .State.serviceName }}-data + target: /data/db + volume: + nocopy: true + info_logs: | + - "{{.Uid}}: To connect to mongo: \"docker exec -ti {{ .ComposeProjectName }}-{{ .State.serviceName }}-1 mongosh -u {{ .State.username }} -p {{ .State.password }}\"" + expected_outputs: + - host + - port + - username + - password + - connection \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mssql/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/mssql/score-compose/provisioners.yaml new file mode 100644 index 00000000..0be30057 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mssql/score-compose/provisioners.yaml @@ -0,0 +1,54 @@ +- uri: template://default-provisioners/mssql + type: mssql + description: Provisions a dedicated database on a shared MS SQL server instance. + init: | + randomPassword: {{ randAlphaNum 16 | quote }} + sk: default-provisioners-mssql + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | quote }} + state: | + service: {{ .Init.sk }} + database: master + username: sa + password: {{ dig "password" .Init.randomPassword .State | quote }} + publishPort: {{ .Init.publishPort }} + outputs: | + server: {{ .State.service }} + port: {{ .State.publishPort }} + connection: "Server=tcp:{{ .State.service }},1433;Initial Catalog={{ .State.database }};User ID={{ .State.username }};Password={{ .State.password }};" + database: {{ .State.database }} + username: {{ .State.username }} + password: {{ .State.password }} + volumes: | + {{ .Init.sk }}-data: + driver: local + services: | + {{ .Init.sk }}: + image: mcr.microsoft.com/mssql/server:latest + restart: always + environment: + ACCEPT_EULA: "Y" + MSSQL_ENABLE_HADR: "1" + MSSQL_AGENT_ENABLED: "1" + MSSQL_SA_PASSWORD: {{ .State.password }} + {{ if ne .Init.publishPort "0" }} + ports: + - target: 1433 + published: {{ .Init.publishPort }} + {{ end }} + volumes: + - type: volume + source: {{ .Init.sk }}-data + target: /var/opt/mssql + info_logs: | + - "{{.Uid}}: To connect to mssql: \"docker run -it --network {{ .ComposeProjectName }}_default --rm mcr.microsoft.com/mssql/server:latest mysql /opt/mssql-tools/bin/sqlcmd -S localhost -U {{ .State.username }} -p {{ .State.password | squote }}\"" + {{ if ne .Init.publishPort "0" }} + - "{{.Uid}}: Or connect your mssql client to \" + Server=tcp:{{ .State.service }},1433;Initial Catalog={{ .State.database }};User ID={{ .State.username }};Password={{ .State.password }};\"" + {{ end }} + expected_outputs: + - server + - port + - connection + - database + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mssql/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/mssql/score-k8s/provisioners.yaml new file mode 100644 index 00000000..6a22d3b9 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mssql/score-k8s/provisioners.yaml @@ -0,0 +1,133 @@ +- uri: template://default-provisioners/mssql + type: mssql + description: Provisions a dedicated database on a shared MS SQL server instance. + init: | + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: mssql-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + database: master + username: sa + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + server: {{ .State.service }} + port: 1433 + connection: "Server=tcp:{{ .State.service }},1433;Initial Catalog={{ .State.database }};User ID={{ .State.username }};Password={{ encodeSecretRef .State.service "MSSQL_SA_PASSWORD" }}" + database: {{ .State.database }} + username: {{ .State.username }} + password: {{ encodeSecretRef .State.service "MSSQL_SA_PASSWORD" }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + data: + MSSQL_SA_PASSWORD: {{ .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + replicas: 1 + serviceName: {{ .State.service }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + spec: + containers: + - name: mssql-db + image: mcr.microsoft.com/mssql/server:latest + ports: + - name: mssql + containerPort: 1433 + env: + - name: ACCEPT_EULA + value: "Y" + - name: MSSQL_ENABLE_HADR + value: "1" + - name: MSSQL_AGENT_ENABLED + value: "1" + - name: MSSQL_SA_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .State.service }} + key: MSSQL_SA_PASSWORD + volumeMounts: + - name: mssql + mountPath: "/var/opt/mssql" + volumeClaimTemplates: + - metadata: + name: mssql + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + ports: + - port: 1433 + targetPort: 1433 + expected_outputs: + - server + - port + - connection + - database + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mysql/score-compose/README.md b/gen/external-content/resource-provisioners/default/mysql/score-compose/README.md new file mode 100644 index 00000000..4490c5d0 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mysql/score-compose/README.md @@ -0,0 +1 @@ +The default mysql provisioner adds a mysql instance and then ensures that the required databases are created on startup. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mysql/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/mysql/score-compose/provisioners.yaml new file mode 100644 index 00000000..d184a6d0 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mysql/score-compose/provisioners.yaml @@ -0,0 +1,85 @@ +- uri: template://default-provisioners/mysql + # By default, match all mysql types regardless of class and id. If you want to override this, create another + # provisioner definition with a higher priority. + type: mysql + description: Provisions a dedicated MySQL database on a shared instance. + # Init template has the random service name and password if needed later + init: | + randomServiceName: mysql-{{ randAlphaNum 6 }} + randomDatabase: db{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + randomRootPassword: {{ randAlphaNum 16 | quote }} + sk: default-provisioners-mysql-instance + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | quote }} + # The state for each database resource is a unique db name and credentials + state: | + database: {{ dig "database" .Init.randomDatabase .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + # All instances agree on the shared state since there is no concurrency here + shared: | + {{ .Init.sk }}: + instanceServiceName: {{ dig .Init.sk "instanceServiceName" .Init.randomServiceName .Shared | quote }} + instancePassword: {{ dig .Init.sk "instancePassword" .Init.randomPassword .Shared | quote }} + instanceRootPassword: {{ dig .Init.sk "instanceRootPassword" .Init.randomRootPassword .Shared | quote }} + # The outputs are the core database outputs. We output both name and database for broader compatibility. + outputs: | + host: {{ dig .Init.sk "instanceServiceName" "" .Shared }} + port: 3306 + name: {{ .State.database }} + database: {{ .State.database }} + username: {{ .State.username }} + password: {{ .State.password }} + # Write out an idempotent create script per database + files: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-db-scripts/{{ .State.database }}.sql: | + CREATE DATABASE IF NOT EXISTS {{ .State.database }}; + USE {{ .State.database }}; + CREATE USER IF NOT EXISTS '{{ .State.username }}'@'localhost' IDENTIFIED BY '{{ .State.password }}'; + GRANT ALL PRIVILEGES ON {{ .State.database }} TO '{{ .State.username }}'@'localhost'; + FLUSH PRIVILEGES; + # Ensure the data volume exists + volumes: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data: + driver: local + # Create an services with the database itself + services: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + image: mirror.gcr.io/mysql:8 + restart: always + environment: + MYSQL_DATABASE: {{ .State.database }} + MYSQL_USER: {{ .State.username }} + MYSQL_PASSWORD: {{ dig .Init.sk "instancePassword" "" .Shared | quote }} + MYSQL_ROOT_PASSWORD: {{ dig .Init.sk "instanceRootPassword" "" .Shared | quote }} + {{ if ne .Init.publishPort "0" }} + ports: + - target: 3306 + published: {{ .Init.publishPort }} + {{ end }} + volumes: + - type: bind + source: {{ .MountsDirectory }}/{{ dig .Init.sk "instanceServiceName" "" .Shared }}-db-scripts + target: /docker-entrypoint-initdb.d/ + - type: volume + source: {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data + target: /var/lib/mysql + healthcheck: + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p$${MYSQL_ROOT_PASSWORD}"] + interval: 5s + timeout: 3s + retries: 10 + info_logs: | + - "{{.Uid}}: To connect to mysql, enter password {{ .State.password | squote }} at: \"docker run -it --network {{ .ComposeProjectName }}_default --rm mysql:8 mysql -h {{ dig .Init.sk "instanceServiceName" "" .Shared }} -u {{ .State.username }} -p\"" + {{ if ne .Init.publishPort "0" }} + - "{{.Uid}}: Or connect your mysql client to \" + mysql://{{ .State.username }}:{{ .State.password }}@{{ dig .Init.sk "instanceServiceName" "" .Shared }}:{{ .Init.publishPort }}/{{ .State.database }}\"" + {{ end }} + expected_outputs: + - host + - port + - name + - database + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/mysql/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/mysql/score-k8s/provisioners.yaml new file mode 100644 index 00000000..d56b8e7f --- /dev/null +++ b/gen/external-content/resource-provisioners/default/mysql/score-k8s/provisioners.yaml @@ -0,0 +1,147 @@ +- uri: template://default-provisioners/mysql + type: mysql + description: Provisions a dedicated MySQL database on a shared instance. + init: | + randomDatabase: db-{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: mysql-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + database: {{ dig "database" .Init.randomDatabase .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.service }} + port: 3306 + name: {{ .State.database }} + database: {{ .State.database }} + username: {{ .State.username }} + password: {{ encodeSecretRef .State.service "MYSQL_PASSWORD" }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + data: + MYSQL_PASSWORD: {{ .State.password | b64enc }} + MYSQL_ROOT_PASSWORD: {{ .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + replicas: 1 + serviceName: {{ .State.service }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + spec: + containers: + - name: mysql-db + image: mirror.gcr.io/mysql:8 + ports: + - name: mysql + containerPort: 3306 + env: + - name: MYSQL_USER + value: {{ .State.username | quote }} + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .State.service }} + key: MYSQL_PASSWORD + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .State.service }} + key: MYSQL_ROOT_PASSWORD + - name: MYSQL_DATABASE + value: {{ .State.database | quote }} + volumeMounts: + - name: data + mountPath: /var/lib/mysql + readinessProbe: + exec: + command: + - mysqladmin + - ping + - -h + - localhost + periodSeconds: 3 + volumeClaimTemplates: + - metadata: + name: data + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + ports: + - port: 3306 + targetPort: 3306 + expected_outputs: + - host + - port + - name + - database + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/postgres-instance/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/postgres-instance/score-compose/provisioners.yaml new file mode 100644 index 00000000..9311c70d --- /dev/null +++ b/gen/external-content/resource-provisioners/default/postgres-instance/score-compose/provisioners.yaml @@ -0,0 +1,58 @@ +- uri: template://default-provisioners/postgres-instance + + type: postgres-instance + description: Provisions a dedicated PostgreSQL instance. + # Init template has the random service name and password if needed later + init: | + randomServiceName: pg-{{ randAlphaNum 6 }} + randomDatabase: db-{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + sk: default-provisioners-postgres-instance + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | quote }} + # The state for each database resource is a unique db name and credentials + state: | + serviceName: {{ dig "serviceName" .Init.randomServiceName .State | quote }} + database: "postgres" + username: "postgres" + password: {{ dig "password" .Init.randomPassword .State | quote }} + # The outputs are the core database outputs. We output both name and database for broader compatibility. + outputs: | + host: {{ .State.serviceName }} + port: 5432 + username: postgres + password: {{ .State.password }} + # Ensure the data volume exists + volumes: | + {{ .State.serviceName }}-data: + driver: local + # Create 2 services, the first is the database itself, the second is the init container which runs the scripts + services: | + {{ .State.serviceName }}: + image: mirror.gcr.io/postgres:17-alpine + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: {{ .State.password | quote }} + {{ if ne .Init.publishPort "0" }} + ports: + - target: 5432 + published: {{ .Init.publishPort }} + {{ end }} + volumes: + - type: volume + source: {{ .State.serviceName }}-data + target: /var/lib/postgresql/data + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + + info_logs: | + - "{{.Uid}}: To connect to postgres, enter password {{ .State.password | squote }} at: \"docker run -it --network {{ .ComposeProjectName }}_default --rm postgres:17-alpine psql -h {{ .State.serviceName }} -U {{ .State.username }} --dbname {{ .State.database }}\"" + {{ if ne .Init.publishPort "0" }} + - "{{.Uid}}: Or connect your postgres client to \"postgres://{{ .State.username }}:{{ .State.password }}@localhost:{{ .Init.publishPort }}/{{ .State.database }}\"" + {{ end }} + expected_outputs: + - host + - port + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/postgres-instance/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/postgres-instance/score-k8s/provisioners.yaml new file mode 100644 index 00000000..8237cca9 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/postgres-instance/score-k8s/provisioners.yaml @@ -0,0 +1,148 @@ +- uri: template://default-provisioners/postgres-instance + type: postgres-instance + description: Provisions a dedicated PostgreSQL instance. + init: | + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: pg-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.service }} + port: 5432 + username: {{ .State.username }} + password: {{ encodeSecretRef .State.service "password" }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + data: + password: {{ .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + replicas: 1 + serviceName: {{ .State.service }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + spec: + automountServiceAccountToken: false + containers: + - name: postgres-db + image: mirror.gcr.io/postgres:17-alpine + ports: + - name: postgres + containerPort: 5432 + env: + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + - name: POSTGRES_USER + value: {{ .State.username | quote }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .State.service }} + key: password + volumeMounts: + - name: pv-data + mountPath: /var/lib/postgresql/data + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: false + privileged: false + capabilities: + drop: + - ALL + readinessProbe: + exec: + command: + - pg_isready + - -U + - {{ .State.username | quote }} + periodSeconds: 3 + securityContext: + runAsNonRoot: true + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + volumeClaimTemplates: + - metadata: + name: pv-data + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + ports: + - port: 5432 + targetPort: 5432 + expected_outputs: + - host + - port + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/postgres/score-compose/README.md b/gen/external-content/resource-provisioners/default/postgres/score-compose/README.md new file mode 100644 index 00000000..dbfd4852 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/postgres/score-compose/README.md @@ -0,0 +1 @@ +The default postgres provisioner adds a postgres instance and then ensures that the required databases are created on startup. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/postgres/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/postgres/score-compose/provisioners.yaml new file mode 100644 index 00000000..3d7f8ae5 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/postgres/score-compose/provisioners.yaml @@ -0,0 +1,97 @@ +- uri: template://default-provisioners/postgres + # By default, match all redis types regardless of class and id. If you want to override this, create another + # provisioner definition with a higher priority. + type: postgres + description: Provisions a dedicated database on a shared PostgreSQL instance. + # Init template has the random service name and password if needed later + init: | + randomServiceName: pg-{{ randAlphaNum 6 }} + randomDatabase: db-{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + sk: default-provisioners-postgres-instance + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | quote }} + # The state for each database resource is a unique db name and credentials + state: | + database: {{ dig "database" .Init.randomDatabase .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + # All instances agree on the shared state since there is no concurrency here + shared: | + {{ .Init.sk }}: + instanceServiceName: {{ dig .Init.sk "instanceServiceName" .Init.randomServiceName .Shared | quote }} + instancePassword: {{ dig .Init.sk "instancePassword" .Init.randomPassword .Shared | quote }} + # The outputs are the core database outputs. We output both name and database for broader compatibility. + outputs: | + host: {{ dig .Init.sk "instanceServiceName" "" .Shared }} + port: 5432 + name: {{ .State.database }} + database: {{ .State.database }} + username: {{ .State.username }} + password: {{ .State.password }} + # Write out an idempotent create script per database + files: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-db-scripts/{{ .State.database }}.sql: | + SELECT 'CREATE DATABASE "{{ .State.database }}"' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '{{ .State.database }}')\gexec + SELECT $$CREATE USER "{{ .State.username }}" WITH PASSWORD '{{ .State.password }}'$$ WHERE NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '{{ .State.username }}')\gexec + GRANT ALL PRIVILEGES ON DATABASE "{{ .State.database }}" TO "{{ .State.username }}"; + \connect "{{ .State.database }}"; + GRANT ALL ON SCHEMA public TO "{{ .State.username }}"; + # Ensure the data volume exists + volumes: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data: + driver: local + # Create 2 services, the first is the database itself, the second is the init container which runs the scripts + services: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + image: mirror.gcr.io/postgres:17-alpine + restart: always + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: {{ dig .Init.sk "instancePassword" "" .Shared | quote }} + {{ if ne .Init.publishPort "0" }} + ports: + - target: 5432 + published: {{ .Init.publishPort }} + {{ end }} + volumes: + - type: volume + source: {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data + target: /var/lib/postgresql/data + healthcheck: + test: ["CMD", "pg_isready", "-U", "postgres"] + interval: 2s + timeout: 2s + retries: 15 + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-init: + image: mirror.gcr.io/postgres:17-alpine + entrypoint: ["/bin/sh"] + environment: + POSTGRES_PASSWORD: {{ dig .Init.sk "instancePassword" "" .Shared | quote }} + command: + - "-c" + - | + cd /db-scripts + ls db-*.sql | xargs cat | psql "postgresql://postgres:$${POSTGRES_PASSWORD}@{{ dig .Init.sk "instanceServiceName" "" .Shared }}:5432/postgres" + labels: + dev.score.compose.labels.is-init-container: "true" + depends_on: + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + condition: service_healthy + restart: true + volumes: + - type: bind + source: {{ .MountsDirectory }}/{{ dig .Init.sk "instanceServiceName" "" .Shared }}-db-scripts + target: /db-scripts + info_logs: | + - "{{.Uid}}: To connect to postgres, enter password {{ .State.password | squote }} at: \"docker run -it --network {{ .ComposeProjectName }}_default --rm postgres:17-alpine psql -h {{ dig .Init.sk "instanceServiceName" "" .Shared }} -U {{ .State.username }} --dbname {{ .State.database }}\"" + {{ if ne .Init.publishPort "0" }} + - "{{.Uid}}: Or connect your postgres client to \"postgres://{{ .State.username }}:{{ .State.password }}@localhost:{{ .Init.publishPort }}/{{ .State.database }}\"" + {{ end }} + expected_outputs: + - host + - port + - name + - database + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/postgres/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/postgres/score-k8s/provisioners.yaml new file mode 100644 index 00000000..53a7e10d --- /dev/null +++ b/gen/external-content/resource-provisioners/default/postgres/score-k8s/provisioners.yaml @@ -0,0 +1,158 @@ +- uri: template://default-provisioners/postgres + type: postgres + description: Provisions a dedicated database on a shared PostgreSQL instance. + init: | + randomDatabase: db-{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: pg-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + database: {{ dig "database" .Init.randomDatabase .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.service }} + port: 5432 + name: {{ .State.database }} + database: {{ .State.database }} + username: {{ .State.username }} + password: {{ encodeSecretRef .State.service "password" }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + data: + password: {{ .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + replicas: 1 + serviceName: {{ .State.service }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + spec: + automountServiceAccountToken: false + containers: + - name: postgres-db + image: mirror.gcr.io/postgres:17-alpine + ports: + - name: postgres + containerPort: 5432 + env: + - name: PGDATA + value: /var/lib/postgresql/data/pgdata + - name: POSTGRES_USER + value: {{ .State.username | quote }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .State.service }} + key: password + - name: POSTGRES_DB + value: {{ .State.database | quote }} + volumeMounts: + - name: pv-data + mountPath: /var/lib/postgresql/data + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: false + privileged: false + capabilities: + drop: + - ALL + readinessProbe: + exec: + command: + - pg_isready + - -U + - {{ .State.username | quote }} + - -d + - {{ .State.database | quote }} + periodSeconds: 3 + securityContext: + runAsNonRoot: true + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + volumeClaimTemplates: + - metadata: + name: pv-data + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + ports: + - port: 5432 + targetPort: 5432 + expected_outputs: + - host + - port + - name + - database + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/rabbitmq/score-compose/README.md b/gen/external-content/resource-provisioners/default/rabbitmq/score-compose/README.md new file mode 100644 index 00000000..e6996d36 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/rabbitmq/score-compose/README.md @@ -0,0 +1 @@ +The default AMQP provisioner provides a simple rabbitmq instance with default configuration and plugins. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/rabbitmq/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/rabbitmq/score-compose/provisioners.yaml new file mode 100644 index 00000000..22e3c820 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/rabbitmq/score-compose/provisioners.yaml @@ -0,0 +1,92 @@ +- uri: template://default-provisioners/rabbitmq + type: amqp + description: Provisions a dedicated RabbitMQ vhost on a shared instance. + init: | + randomServiceName: rabbitmq-{{ randAlphaNum 6 }} + randomVHost: vhost-{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + sk: default-provisioners-rabbitmq + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | atoi }} + publishManagementPort: {{ dig "annotations" "compose.score.dev/publish-management-port" "0" .Metadata | atoi }} + state: | + vhost: {{ dig "vhost" .Init.randomVHost .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ dig .Init.sk "instanceServiceName" "" .Shared }} + port: 5672 + vhost: {{ .State.vhost }} + username: {{ .State.username }} + password: {{ .State.password }} + shared: | + {{ .Init.sk }}: + instanceServiceName: {{ dig .Init.sk "instanceServiceName" .Init.randomServiceName .Shared | quote }} + instanceErlangCookie: {{ dig .Init.sk "instanceErlangCookie" (randAlpha 20) .Shared }} + {{ $publishPorts := (list) }} + {{ if ne .Init.publishPort 0 }}{{ $publishPorts = (append $publishPorts (dict "target" 5672 "published" .Init.publishPort)) }}{{ end }} + {{ $x := (dig "annotations" "compose.score.dev/publish-management-port" "0" .Metadata | atoi) }} + {{ if ne .Init.publishManagementPort 0 }}{{ $publishPorts = (append $publishPorts (dict "target" 15672 "published" .Init.publishManagementPort)) }}{{ end }} + publishPorts: {{ $publishPorts | toJson }} + volumes: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data: + driver: local + files: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-db-scripts/{{ .State.vhost }}.sh: | + while ! rabbitmqctl list_vhosts > /dev/null 2>&1; do + sleep 1 + done + rabbitmqctl list_vhosts | grep {{ .State.vhost }} || rabbitmqctl add_vhost {{ .State.vhost }} + rabbitmqctl list_users | grep {{ .State.username }} || rabbitmqctl add_user {{ .State.username }} {{ .State.password }} + rabbitmqctl set_user_tags {{ .State.username }} administrator + rabbitmqctl set_permissions -p {{ .State.vhost }} {{ .State.username }} ".*" ".*" ".*" + rabbitmqctl set_topic_permissions -p {{ .State.vhost }} {{ .State.username }} ".*" ".*" ".*" + services: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + image: mirror.gcr.io/rabbitmq:3-management-alpine + restart: always + environment: + RABBITMQ_ERLANG_COOKIE: {{ dig .Init.sk "instanceErlangCookie" "" .Shared }} + RABBITMQ_DEFAULT_USER: guest + RABBITMQ_DEFAULT_PASS: guest + ports: {{ dig .Init.sk "publishPorts" "" .Shared | toJson}} + volumes: + - type: volume + source: {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data + target: /var/lib/rabbitmq + healthcheck: + test: ["CMD-SHELL", "rabbitmq-diagnostics -q check_port_connectivity"] + interval: 2s + timeout: 5s + retries: 15 + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-init: + image: mirror.gcr.io/rabbitmq:3-management-alpine + entrypoint: ["/bin/sh"] + environment: + RABBITMQ_ERLANG_COOKIE: {{ dig .Init.sk "instanceErlangCookie" "" .Shared }} + command: + - "-c" + - | + set -exu + for s in /db-scripts/*.sh; do source $$s; done + depends_on: + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + condition: service_healthy + restart: true + labels: + dev.score.compose.labels.is-init-container: "true" + network_mode: service:{{ dig .Init.sk "instanceServiceName" "" .Shared }} + volumes: + - type: bind + source: {{ .MountsDirectory }}/{{ dig .Init.sk "instanceServiceName" "" .Shared }}-db-scripts + target: /db-scripts + info_logs: | + {{ if ne .Init.publishManagementPort 0 }} + - "{{.Uid}}: Browse the rabbitmq UI at \"http://localhost:{{ .Init.publishManagementPort }}\"" + {{ end }} + expected_outputs: + - host + - port + - vhost + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/rabbitmq/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/rabbitmq/score-k8s/provisioners.yaml new file mode 100644 index 00000000..ec03c515 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/rabbitmq/score-k8s/provisioners.yaml @@ -0,0 +1,126 @@ +- uri: template://default-provisioners/rabbitmq + type: amqp + description: Provisions a dedicated RabbitMQ vhost on a shared instance. + init: | + randomVHost: vhost-{{ randAlpha 8 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: rabbitmq-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + vhost: {{ dig "vhost" .Init.randomVHost .State | quote }} + username: {{ dig "username" .Init.randomUsername .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.service }} + port: 5672 + vhost: {{ .State.vhost }} + username: {{ .State.username }} + password: {{ .State.password }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }}-secret + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }}-secret + app.kubernetes.io/instance: {{ .State.service }} + data: + RABBITMQ_DEFAULT_VHOST: {{ .State.vhost | b64enc }} + RABBITMQ_DEFAULT_USER: {{ .State.username | b64enc }} + RABBITMQ_DEFAULT_PASS: {{ .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + serviceName: {{ .State.service }} + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + containers: + - name: rabbitmq + image: mirror.gcr.io/rabbitmq:3-management-alpine + ports: + - name: amqp + containerPort: 5672 + - name: management + containerPort: 15672 + envFrom: + - secretRef: + name: {{ .State.service }}-secret + volumeMounts: + - name: data + mountPath: /var/lib/rabbitmq + readinessProbe: + exec: + command: + - rabbitmq-diagnostics + - -q + - check_port_connectivity + periodSeconds: 3 + initialDelaySeconds: 30 + timeoutSeconds: 5 + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 3Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + ports: + - port: 5672 + targetPort: 5672 + name: amqp + - port: 15672 + targetPort: 15672 + name: management + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + expected_outputs: + - host + - port + - vhost + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/redis/score-compose/README.md b/gen/external-content/resource-provisioners/default/redis/score-compose/README.md new file mode 100644 index 00000000..3b9f84c7 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/redis/score-compose/README.md @@ -0,0 +1 @@ +The default redis provisioner adds a redis service to the project which returns a host, port, username, and password. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/redis/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/redis/score-compose/provisioners.yaml new file mode 100644 index 00000000..4b3fafa2 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/redis/score-compose/provisioners.yaml @@ -0,0 +1,60 @@ +- uri: template://default-provisioners/redis + # By default, match all redis types regardless of class and id. If you want to override this, create another + # provisioner definition with a higher priority. + type: redis + description: Provisions a dedicated Redis instance. + # Init template has the default port and a random service name and password if needed later + init: | + port: 6379 + randomServiceName: redis-{{ randAlphaNum 6 }} + randomPassword: {{ randAlphaNum 16 | quote }} + # The only state we need to persist is the chosen random service name and password + state: | + serviceName: {{ dig "serviceName" .Init.randomServiceName .State | quote }} + password: {{ dig "password" .Init.randomPassword .State | quote }} + # Return the outputs schema that consumers expect + outputs: | + host: {{ .State.serviceName }} + port: {{ .Init.port }} + username: default + password: {{ .State.password | quote }} + # write the config file to the mounts directory + files: | + {{ .State.serviceName }}/redis.conf: | + requirepass {{ .State.password }} + port {{ .Init.port }} + save 60 1 + loglevel warning + # add a volume for persistence of the redis data + volumes: | + {{ .State.serviceName }}-data: + name: {{ .State.serviceName }}-data + driver: local + labels: + dev.score.compose.res.uid: {{ .Uid }} + # And the redis service itself with volumes bound in + services: | + {{ .State.serviceName }}: + labels: + dev.score.compose.res.uid: {{ .Uid }} + image: mirror.gcr.io/redis:7-alpine + restart: always + entrypoint: ["redis-server"] + command: ["/usr/local/etc/redis/redis.conf"] + volumes: + - type: bind + source: {{ .MountsDirectory }}/{{ .State.serviceName }}/redis.conf + target: /usr/local/etc/redis/redis.conf + read_only: true + - type: volume + source: {{ .State.serviceName }}-data + target: /data + volume: + nocopy: true + info_logs: | + - "{{.Uid}}: To connect to redis: \"docker run -it --network {{ .ComposeProjectName }}_default --rm redis redis-cli -h {{ .State.serviceName | squote }} -a {{ .State.password | squote }}\"" + expected_outputs: + - host + - port + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/redis/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/redis/score-k8s/provisioners.yaml new file mode 100644 index 00000000..1bead531 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/redis/score-k8s/provisioners.yaml @@ -0,0 +1,147 @@ +- uri: template://default-provisioners/redis + type: redis + description: Provisions a dedicated Redis instance. + init: | + randomPassword: {{ randAlphaNum 16 | quote }} + state: | + service: redis-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + username: default + password: {{ dig "password" .Init.randomPassword .State | quote }} + outputs: | + host: {{ .State.service }} + port: 6379 + username: {{ .State.username }} + password: {{ encodeSecretRef .State.service "password" }} + manifests: | + - apiVersion: v1 + kind: Secret + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + data: + password: {{ .State.password | b64enc }} + redis.conf: {{ printf "requirepass %s\nport 6379\nsave 60 1\nloglevel warning\n" .State.password | b64enc }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + replicas: 1 + serviceName: {{ .State.service }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ .State.service }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + spec: + automountServiceAccountToken: false + containers: + - name: redis + image: mirror.gcr.io/redis:7-alpine + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true + ports: + - name: redis + containerPort: 6379 + volumeMounts: + - name: redis-data + mountPath: /data + - name: config + mountPath: /usr/local/etc/redis + readinessProbe: + exec: + command: + - redis-cli + - ping + periodSeconds: 3 + securityContext: + fsGroup: 1000 + runAsGroup: 1000 + runAsNonRoot: true + runAsUser: 1000 + seccompProfile: + type: RuntimeDefault + volumes: + - name: config + secret: + secretName: {{ .State.service }} + items: + - key: redis.conf + path: redis.conf + volumeClaimTemplates: + - metadata: + name: redis-data + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Service + metadata: + name: {{ .State.service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.service }} + app.kubernetes.io/instance: {{ .State.service }} + spec: + selector: + app.kubernetes.io/instance: {{ .State.service }} + type: ClusterIP + ports: + - port: 6379 + targetPort: 6379 + expected_outputs: + - host + - port + - username + - password \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/route/score-compose/README.md b/gen/external-content/resource-provisioners/default/route/score-compose/README.md new file mode 100644 index 00000000..027715e4 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/route/score-compose/README.md @@ -0,0 +1 @@ +The default route provisioner sets up an nginx service with an HTTP service that can route on our prefix paths. It assumes the hostnames and routes provided have no overlaps. Weird behavior may happen if there are overlaps. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/route/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/route/score-compose/provisioners.yaml new file mode 100644 index 00000000..ab76ab91 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/route/score-compose/provisioners.yaml @@ -0,0 +1,104 @@ +- uri: template://default-provisioners/route + type: route + description: Provisions a ingress route on a shared Nginx instance. + init: | + randomServiceName: routing-{{ randAlphaNum 6 }} + sk: default-provisioners-routing-instance + {{ if not (regexMatch "^/|(/([^/]+))+$" .Params.path) }}{{ fail "params.path start with a / but cannot end with /" }}{{ end }} + {{ if not (regexMatch "^[a-z0-9_.-]{1,253}$" .Params.host) }}{{ fail (cat "params.host must be a valid hostname but was" .Params.host) }}{{ end }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ if not $ports }}{{ fail "no service ports exist" }}{{ end }} + {{ $port := index $ports (print .Params.port) }} + {{ if not $port.TargetPort }}{{ fail "params.port is not a named service port" }}{{ end }} + + shared: | + {{ .Init.sk }}: + instancePort: {{ dig .Init.sk "instancePort" 8080 .Shared }} + instanceServiceName: {{ dig .Init.sk "instanceServiceName" .Init.randomServiceName .Shared | quote }} + {{ $targetHost := (index .WorkloadServices .SourceWorkload).ServiceName }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ $port := index $ports (print .Params.port) }} + {{ $targetPort := $port.TargetPort }} + {{ $target := (printf "%s:%d" $targetHost $targetPort) }} + {{ $hBefore := dig .Init.sk "hosts" (dict) .Shared }} + {{ $rBefore := dig .Params.host (dict) $hBefore }} + {{ $pathType := dig "compose.score.dev/route-provisioner-path-type" "Prefix" (dig "annotations" (dict) (.Metadata | default (dict))) }} + {{ $inner := dict "path" .Params.path "target" $target "port" $targetPort "path_type" $pathType }} + {{ $rAfter := (merge $rBefore (dict .Uid $inner)) }} + {{ $hAfter := (merge $hBefore (dict .Params.host $rAfter)) }} + hosts: {{ $hAfter | toRawJson }} + files: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}/nginx.conf: | + worker_processes 1; + worker_rlimit_nofile 8192; + events { + worker_connections 4096; + } + http { + resolver 127.0.0.11; + + {{ range $h, $r := (dig .Init.sk "hosts" "" .Shared) }} + server { + listen 80; + listen [::]:80; + server_name {{ $h }}; + + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $remote_addr; + proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for; + proxy_set_header Proxy ""; + proxy_connect_timeout 5s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + proxy_buffers 16 4k; + proxy_buffer_size 2k; + client_max_body_size 10m; + {{ dig "compose.score.dev/route-provisioner-server-snippet" "" (dig "annotations" (dict) ($.Metadata | default (dict))) | indent 4 }} + + location = /favicon.ico { + return 204; + access_log off; + log_not_found off; + } + + {{ range $k, $v := $r }} + # the basic path variant, "/" or "/one/two" + location ~ ^{{ index $v "path" }}$ { + set $backend {{ index $v "target" }}; + proxy_pass http://$backend; + {{ dig "compose.score.dev/route-provisioner-location-snippet" "" (dig "annotations" (dict) ($.Metadata | default (dict))) | indent 6 }} + } + + # The prefix match variants are included by default but can be excluded via 'compose.score.dev/route-provisioner-path-type' annotation + {{ if eq (index $v "path_type") "Prefix" }} + location ~ ^{{ index $v "path" | trimSuffix "/" }}/.* { + set $backend {{ index $v "target" }}; + proxy_pass http://$backend; + {{ dig "compose.score.dev/route-provisioner-location-snippet" "" (dig "annotations" (dict) ($.Metadata | default (dict))) | indent 6 }} + } + {{ end }} + {{ end }} + } + {{ end }} + } + + services: | + {{ $p := (dig .Init.sk "instancePort" 0 .Shared) }} + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + image: mirror.gcr.io/nginx:1-alpine + restart: always + ports: + - published: {{ $p }} + target: 80 + volumes: + - type: bind + source: {{ .MountsDirectory }}/{{ dig .Init.sk "instanceServiceName" "" .Shared }}/nginx.conf + target: /etc/nginx/nginx.conf + readOnly: true + info_logs: | + {{ $p := (dig .Init.sk "instancePort" 0 .Shared) }} + - "{{.Uid}}: To connect to this route, http://{{ .Params.host }}:{{ $p }}{{ .Params.path }} (make sure {{ .Params.host }} resolves to localhost)" + supported_params: + - host + - port + - path \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/route/score-k8s/README.md b/gen/external-content/resource-provisioners/default/route/score-k8s/README.md new file mode 100644 index 00000000..d9fb30ce --- /dev/null +++ b/gen/external-content/resource-provisioners/default/route/score-k8s/README.md @@ -0,0 +1 @@ +Routes could be implemented as either traditional ingress resources or using the newer gateway API. In this default provisioner we use the gateway API with some sensible defaults. But you may wish to replace this. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/route/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/route/score-k8s/provisioners.yaml new file mode 100644 index 00000000..34bc23a5 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/route/score-k8s/provisioners.yaml @@ -0,0 +1,45 @@ +- uri: template://default-provisioners/route + type: route + description: Provisions an HTTPRoute on a shared Nginx instance. + init: | + {{ if not (regexMatch "^/|(/([^/]+))+$" .Params.path) }}{{ fail "params.path start with a / but cannot end with /" }}{{ end }} + {{ if not (regexMatch "^[a-z0-9_.-]{1,253}$" .Params.host) }}{{ fail (cat "params.host must be a valid hostname but was" .Params.host) }}{{ end }} + {{ $ports := (index .WorkloadServices .SourceWorkload).Ports }} + {{ if not $ports }}{{ fail "no service ports exist" }}{{ end }} + {{ $port := index $ports (print .Params.port) }} + {{ if not $port.TargetPort }}{{ fail "params.port is not a named service port" }}{{ end }} + state: | + routeName: route-{{ .SourceWorkload }}-{{ substr 0 8 .Guid | lower }} + manifests: | + - apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: {{ .State.routeName }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + annotations: + k8s.score.dev/source-workload: {{ .SourceWorkload }} + k8s.score.dev/resource-uid: {{ .Uid }} + k8s.score.dev/resource-guid: {{ .Guid }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ .State.routeName }} + app.kubernetes.io/instance: {{ .State.routeName }} + spec: + parentRefs: + - name: default + hostnames: + - {{ .Params.host | quote }} + rules: + - matches: + - path: + type: PathPrefix + value: {{ .Params.path | quote }} + backendRefs: + - name: {{ (index .WorkloadServices .SourceWorkload).ServiceName }} + port: {{ .Params.port }} + supported_params: + - host + - port + - path \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/s3/score-compose/README.md b/gen/external-content/resource-provisioners/default/s3/score-compose/README.md new file mode 100644 index 00000000..4845e478 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/s3/score-compose/README.md @@ -0,0 +1 @@ +This resource provides a minio based S3 bucket with AWS-style credentials. This provides some common and well known outputs that can be used with any generic AWS s3 client. If the provider has a publish port annotation, it can expose a management port on the local network for debugging and connectivity. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/s3/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/s3/score-compose/provisioners.yaml new file mode 100644 index 00000000..92426a9e --- /dev/null +++ b/gen/external-content/resource-provisioners/default/s3/score-compose/provisioners.yaml @@ -0,0 +1,100 @@ +- uri: template://default-provisioners/s3 + type: s3 + description: Provisions a dedicated S3 bucket with AWS-style credentials on a shared MinIO instance. + # The init template contains some initial seed data that can be used it needed. + init: | + randomServiceName: minio-{{ randAlphaNum 6 }} + randomUsername: user-{{ randAlpha 8 }} + randomPassword: {{ randAlphaNum 16 | quote }} + randomBucket: bucket-{{ randAlpha 8 | lower }} + randomAccessKeyId: {{ randAlphaNum 20 | quote }} + randomSecretKey: {{ randAlphaNum 40 | quote }} + sk: default-provisioners-minio-instance + publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | atoi }} + # The only instance state is the bucket name, for now we provision a single aws key across s3 resources. + state: | + bucket: {{ dig "bucket" .Init.randomBucket .State | quote }} + # The shared state contains the chosen service name and credentials + shared: | + {{ .Init.sk }}: + instanceServiceName: {{ dig .Init.sk "instanceServiceName" .Init.randomServiceName .Shared | quote }} + instanceUsername: {{ dig .Init.sk "instanceUsername" .Init.randomUsername .Shared | quote }} + instancePassword: {{ dig .Init.sk "instancePassword" .Init.randomPassword .Shared | quote }} + instanceAccessKeyId: {{ dig .Init.sk "instanceAccessKeyId" .Init.randomAccessKeyId .Shared | quote }} + instanceSecretKey: {{ dig .Init.sk "instanceSecretKey" .Init.randomSecretKey .Shared | quote }} + publishPort: {{ with (dig .Init.sk "publishPort" 0 .Shared) }}{{ if ne . 0 }}{{ . }}{{ else }}{{ $.Init.publishPort }}{{ end }}{{ end }} + # the outputs that we can expose + outputs: | + bucket: {{ .State.bucket }} + access_key_id: {{ dig .Init.sk "instanceAccessKeyId" "" .Shared | quote }} + secret_key: {{ dig .Init.sk "instanceSecretKey" "" .Shared | quote }} + endpoint: http://{{ dig .Init.sk "instanceServiceName" "" .Shared }}:9000 + # for compatibility with Humanitec's existing s3 resource + region: "us-east-1" + aws_access_key_id: {{ dig .Init.sk "instanceAccessKeyId" "" .Shared | quote }} + aws_secret_key: {{ dig .Init.sk "instanceSecretKey" "" .Shared | quote }} + # we store 2 files, 1 is always the same and overridden, the other is per bucket + files: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-setup-scripts/00-svcacct.sh: | + set -eu + mc alias set myminio http://{{ dig .Init.sk "instanceServiceName" "" .Shared }}:9000 {{ dig .Init.sk "instanceUsername" "" .Shared }} {{ dig .Init.sk "instancePassword" "" .Shared }} + mc admin user svcacct info myminio {{ dig .Init.sk "instanceAccessKeyId" "" .Shared | quote }} || mc admin user svcacct add myminio {{ dig .Init.sk "instanceUsername" "" .Shared | quote }} --access-key {{ dig .Init.sk "instanceAccessKeyId" "" .Shared | quote }} --secret-key {{ dig .Init.sk "instanceSecretKey" "" .Shared | quote }} + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-setup-scripts/10-bucket-{{ .State.bucket }}.sh: | + set -eu + mc alias set myminio http://{{ dig .Init.sk "instanceServiceName" "" .Shared }}:9000 {{ dig .Init.sk "instanceUsername" "" .Shared }} {{ dig .Init.sk "instancePassword" "" .Shared }} + mc mb -p myminio/{{ .State.bucket }} + volumes: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data: + driver: local + # 2 services, the minio one, and the init container which ensures the service account and buckets exist + services: | + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + image: quay.io/minio/minio + command: ["server", "/data", "--console-address", ":9001"] + restart: always + {{ if ne .Init.publishPort 0 }} + ports: + - target: 9001 + published: {{ .Init.publishPort }} + {{ end }} + healthcheck: + test: ["CMD-SHELL", "mc alias set myminio http://localhost:9000 {{ dig .Init.sk "instanceUsername" "" .Shared }} {{ dig .Init.sk "instancePassword" "" .Shared }}"] + interval: 2s + timeout: 2s + retries: 15 + environment: + MINIO_ROOT_USER: {{ dig .Init.sk "instanceUsername" "" .Shared | quote }} + MINIO_ROOT_PASSWORD: {{ dig .Init.sk "instancePassword" "" .Shared | quote }} + volumes: + - type: volume + source: {{ dig .Init.sk "instanceServiceName" "" .Shared }}-data + target: /data + {{ dig .Init.sk "instanceServiceName" "" .Shared }}-init: + image: quay.io/minio/minio + entrypoint: ["/bin/bash"] + command: + - "-c" + - "for s in $$(ls /setup-scripts -1); do sh /setup-scripts/$$s; done" + labels: + dev.score.compose.labels.is-init-container: "true" + depends_on: + {{ dig .Init.sk "instanceServiceName" "" .Shared }}: + condition: service_healthy + restart: true + volumes: + - type: bind + source: {{ .MountsDirectory }}/{{ dig .Init.sk "instanceServiceName" "" .Shared }}-setup-scripts + target: /setup-scripts + info_logs: | + - "{{.Uid}}: To connect with a minio client: use the myminio alias at \"docker run -it --network {{ .ComposeProjectName }}_default --rm --entrypoint /bin/bash quay.io/minio/minio -c 'mc alias set myminio http://{{ dig .Init.sk "instanceServiceName" "" .Shared }}:9000 {{ dig .Init.sk "instanceAccessKeyId" "" .Shared }} {{ dig .Init.sk "instanceSecretKey" "" .Shared }}; bash'\"" + {{ if ne .Init.publishPort 0 }} + - "{{.Uid}}: Or enter {{ dig .Init.sk "instanceUsername" "" .Shared }} / {{ dig .Init.sk "instancePassword" "" .Shared }} at https://localhost:{{ .Init.publishPort }}" + {{ end }} + expected_outputs: + - bucket + - access_key_id + - secret_key + - endpoint + - region + - aws_access_key_id + - aws_secret_key \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/s3/score-k8s/README.md b/gen/external-content/resource-provisioners/default/s3/score-k8s/README.md new file mode 100644 index 00000000..e81cfcc6 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/s3/score-k8s/README.md @@ -0,0 +1 @@ +This resource provides an in-cluster minio based S3 bucket with AWS-style credentials. This provides some common and well known outputs that can be used with any generic AWS s3 client. The outputs of the provisioner are a stateful set, a service, a secret, and a job per bucket. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/s3/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/s3/score-k8s/provisioners.yaml new file mode 100644 index 00000000..0b9aa87d --- /dev/null +++ b/gen/external-content/resource-provisioners/default/s3/score-k8s/provisioners.yaml @@ -0,0 +1,180 @@ +- uri: template://default-provisioners/s3 + type: s3 + description: Provisions a dedicated S3 bucket with AWS-style credentials on a shared MinIO instance. + # The init template contains some initial seed data that can be used t needed. + init: | + sk: default-provisioners-minio-instance + state: | + bucket: {{ dig "bucket" (printf "bucket-%s" .Guid) .State | quote }} + shared: | + {{ .Init.sk }}: + instanceServiceName: {{ dig .Init.sk "instanceServiceName" (randAlpha 7 | lower | printf "minio-%s") .Shared | quote }} + instanceUsername: {{ dig .Init.sk "instanceUsername" (randAlpha 7 | printf "user-%s") .Shared | quote }} + instancePassword: {{ dig .Init.sk "instancePassword" (randAlphaNum 16) .Shared | quote }} + instanceAccessKeyId: {{ dig .Init.sk "instanceAccessKeyId" (randAlphaNum 20) .Shared | quote }} + instanceSecretKey: {{ dig .Init.sk "instanceSecretKey" (randAlphaNum 40) .Shared | quote }} + outputs: | + {{ $shared := dig .Init.sk (dict) .Shared }} + {{ $service := $shared.instanceServiceName }} + bucket: {{ .State.bucket }} + access_key_id: {{ $shared.instanceAccessKeyId | quote }} + secret_key: {{ encodeSecretRef $service "secret_key" }} + endpoint: http://{{ $service }}:9000 + region: "us-east-1" + # for compatibility with Humanitec's existing s3 resource + aws_access_key_id: {{ $shared.instanceAccessKeyId | quote }} + aws_secret_key: {{ encodeSecretRef $service "secret_key" }} + manifests: | + {{ $shared := dig .Init.sk (dict) .Shared }} + {{ $service := $shared.instanceServiceName }} + - apiVersion: apps/v1 + kind: StatefulSet + metadata: + name: {{ $service | quote }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ $service | quote }} + app.kubernetes.io/instance: {{ $service | quote }} + spec: + replicas: 1 + serviceName: {{ $service | quote }} + selector: + matchLabels: + app.kubernetes.io/instance: {{ $service | quote }} + template: + metadata: + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ $service | quote }} + app.kubernetes.io/instance: {{ $service | quote }} + spec: + automountServiceAccountToken: false + containers: + - name: minio + image: quay.io/minio/minio + args: ["server", "/data", "--console-address", ":9001"] + ports: + - name: service + containerPort: 9000 + - name: console + containerPort: 9001 + env: + - name: MINIO_ROOT_USER + value: {{ $shared.instanceUsername | quote }} + - name: MINIO_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $service | quote }} + key: password + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: false + privileged: false + capabilities: + drop: + - ALL + volumeMounts: + - name: data + mountPath: /data + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + volumeClaimTemplates: + - metadata: + name: data + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ $service | quote }} + app.kubernetes.io/instance: {{ $service | quote }} + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 1Gi + - apiVersion: v1 + kind: Secret + metadata: + name: {{ $service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ $service }} + app.kubernetes.io/instance: {{ $service }} + data: + password: {{ $shared.instancePassword | b64enc }} + secret_key: {{ $shared.instanceSecretKey | b64enc }} + - apiVersion: v1 + kind: Service + metadata: + name: {{ $service }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + labels: + app.kubernetes.io/managed-by: score-k8s + app.kubernetes.io/name: {{ $service }} + app.kubernetes.io/instance: {{ $service }} + spec: + selector: + app.kubernetes.io/instance: {{ $service }} + type: ClusterIP + ports: + - name: service + port: 9000 + targetPort: 9000 + - name: console + port: 9001 + targetPort: 9001 + - apiVersion: batch/v1 + kind: Job + metadata: + name: {{ printf "%s-bucket-%s" $service .Guid }} + {{ if ne .Namespace "" }} + namespace: {{ .Namespace }} + {{ end }} + labels: + app.kubernetes.io/managed-by: score-k8s + spec: + template: + spec: + restartPolicy: OnFailure + containers: + - name: main + image: quay.io/minio/minio + command: + - /bin/bash + - -c + - | + set -eu + mc alias set myminio http://{{ $service }}:9000 {{ $shared.instanceUsername | quote }} $MINIO_ROOT_PASSWORD + mc admin user svcacct info myminio {{ $shared.instanceAccessKeyId | quote }} || mc admin user svcacct add myminio {{ $shared.instanceUsername | quote }} --access-key {{ $shared.instanceAccessKeyId | quote }} --secret-key $MINIO_SECRET_KEY + mc mb -p myminio/{{ .State.bucket }} + env: + - name: MINIO_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ $service | quote }} + key: password + - name: MINIO_SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ $service | quote }} + key: secret_key + expected_outputs: + - bucket + - access_key_id + - secret_key + - endpoint + - region + - aws_access_key_id + - aws_secret_key \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/service-port/score-compose/README.md b/gen/external-content/resource-provisioners/default/service-port/score-compose/README.md new file mode 100644 index 00000000..18af681b --- /dev/null +++ b/gen/external-content/resource-provisioners/default/service-port/score-compose/README.md @@ -0,0 +1 @@ +The default provisioner for service resources, this expects a workload and port name and will return the hostname and port required to contact it. This will validate that the workload and port exist, but won't enforce a dependency relationship yet. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/service-port/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/service-port/score-compose/provisioners.yaml new file mode 100644 index 00000000..d26642fd --- /dev/null +++ b/gen/external-content/resource-provisioners/default/service-port/score-compose/provisioners.yaml @@ -0,0 +1,18 @@ +- uri: template://default-provisioners/service-port + type: service-port + description: Outputs a hostname and port for connecting to another workload. + outputs: | + {{ if not .Params.workload }}{{ fail "expected 'workload' param for the target workload name" }}{{ end }} + {{ if not .Params.port }}{{ fail "expected 'port' param for the name of the target workload service port" }}{{ end }} + {{ $w := (index .WorkloadServices .Params.workload) }} + {{ if or (not $w) (not $w.ServiceName) }}{{ fail "unknown workload" }}{{ end }} + {{ $p := (index $w.Ports .Params.port) }} + {{ if not $p }}{{ fail "unknown service port" }}{{ end }} + hostname: {{ $w.ServiceName | quote }} + port: {{ $p.TargetPort }} + expected_outputs: + - hostname + - port + supported_params: + - workload + - port \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/service-port/score-k8s/README.md b/gen/external-content/resource-provisioners/default/service-port/score-k8s/README.md new file mode 100644 index 00000000..18af681b --- /dev/null +++ b/gen/external-content/resource-provisioners/default/service-port/score-k8s/README.md @@ -0,0 +1 @@ +The default provisioner for service resources, this expects a workload and port name and will return the hostname and port required to contact it. This will validate that the workload and port exist, but won't enforce a dependency relationship yet. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/service-port/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/service-port/score-k8s/provisioners.yaml new file mode 100644 index 00000000..95f500ed --- /dev/null +++ b/gen/external-content/resource-provisioners/default/service-port/score-k8s/provisioners.yaml @@ -0,0 +1,18 @@ +- uri: template://default-provisioners/service-port + type: service-port + description: Outputs a hostname and port for connecting to another workload. + outputs: | + {{ if not .Params.workload }}{{ fail "expected 'workload' param for the target workload name" }}{{ end }} + {{ if not .Params.port }}{{ fail "expected 'port' param for the name of the target workload service port" }}{{ end }} + {{ if or (not $w) (not $w.ServiceName) }}{{ fail "unknown workload" }}{{ end }} + {{ if not $w }}{{ fail "unknown workload" }}{{ end }} + {{ $p := (index $w.Ports .Params.port) }} + {{ if not $p }}{{ fail "unknown service port" }}{{ end }} + hostname: {{ $w.ServiceName | quote }} + port: {{ $p.TargetPort }} + expected_outputs: + - hostname + - port + supported_params: + - workload + - port \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/volume/score-compose/README.md b/gen/external-content/resource-provisioners/default/volume/score-compose/README.md new file mode 100644 index 00000000..132f96ef --- /dev/null +++ b/gen/external-content/resource-provisioners/default/volume/score-compose/README.md @@ -0,0 +1 @@ +The default volume provisioner provided by score-compose allows basic volume resources to be created in the resources system. The volume resource just creates an ephemeral Docker volume with a random string as the name, and source attribute that we can reference. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/volume/score-compose/provisioners.yaml b/gen/external-content/resource-provisioners/default/volume/score-compose/provisioners.yaml new file mode 100644 index 00000000..0f441ccb --- /dev/null +++ b/gen/external-content/resource-provisioners/default/volume/score-compose/provisioners.yaml @@ -0,0 +1,25 @@ +- uri: template://default-provisioners/volume + # By default, match all classes and ids of volume. If you want to override this, create another provisioner definition + # with a higher priority. + type: volume + description: Creates a persistent volume that can be mounted on a workload. + init: | + randomVolumeName: {{ .Id | replace "." "-" }}-{{ randAlphaNum 6 }} + # Store the random volume name if we haven't chosen one yet, otherwise use the one that exists already + state: | + name: {{ dig "name" .Init.randomVolumeName .State }} + # Return a source value with the volume name. This can be used in volume resource references now. + outputs: | + type: volume + source: {{ .State.name }} + # Add a volume to the docker compose file. We assume our name is unique here. We also apply a label to help ensure + # that we can track the volume back to the workload and resource that created it. + volumes: | + {{ .State.name }}: + name: {{ .State.name }} + driver: local + labels: + dev.score.compose.res.uid: {{ .Uid }} + expected_outputs: + - source + - type \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/volume/score-k8s/README.md b/gen/external-content/resource-provisioners/default/volume/score-k8s/README.md new file mode 100644 index 00000000..034e136a --- /dev/null +++ b/gen/external-content/resource-provisioners/default/volume/score-k8s/README.md @@ -0,0 +1 @@ +As an example we have a 'volume' type which returns an emptyDir volume. In production or for real applications you may want to replace this with a provisioner for a tmpfs, host path, or persistent volume and claims. \ No newline at end of file diff --git a/gen/external-content/resource-provisioners/default/volume/score-k8s/provisioners.yaml b/gen/external-content/resource-provisioners/default/volume/score-k8s/provisioners.yaml new file mode 100644 index 00000000..f3bb17c5 --- /dev/null +++ b/gen/external-content/resource-provisioners/default/volume/score-k8s/provisioners.yaml @@ -0,0 +1,8 @@ +- uri: template://default-provisioners/volume + type: volume + description: Creates a persistent volume that can be mounted on a workload. + outputs: | + source: + emptyDir: {} + expected_outputs: + - source \ No newline at end of file diff --git a/layouts/examples/list.html b/layouts/examples/list.html index 5072e4b3..b791ccf9 100644 --- a/layouts/examples/list.html +++ b/layouts/examples/list.html @@ -62,10 +62,10 @@

{{ end }} {{ if eq (index $section 2) "patch-templates" }} -

@@ -78,12 +78,54 @@

{{ end }}

{{ $excerpt := replaceRE `\[(?P
+ {{ end }} + {{ if eq (index $section 2) "resource-provisioners" }} +
+
+

+ + {{ .Title }} + +

+ {{ if .Params.source }} + {{ .Params.source | humanize }} + {{ end }} + {{ if .Params.resourceType }} + {{ .Params.resourceType }} + {{ end }} + {{ if .Params.implementation }} + {{ .Params.implementation }} + {{ end }} + {{ if .Params.provisionerType }} + {{ .Params.provisionerType }} + {{ end }} + {{ if .Params.tool }} + {{ .Params.tool }} + {{ end }} +
+ {{ $excerpt := replaceRE `\[(?P
{{ end }} {{ end }} diff --git a/layouts/partials/examples/filters.html b/layouts/partials/examples/filters.html index 14c391d8..5064aa60 100644 --- a/layouts/partials/examples/filters.html +++ b/layouts/partials/examples/filters.html @@ -132,4 +132,113 @@

{{ index $labels 0 }}

{{ end }} {{ end }} + {{ if eq $sectionWithoutBase "resource-provisioners" }} + {{ $sectionMetadata := index $metadata "resource-provisioners" }} +
+ {{ with site.GetPage .section }} +
+

{{ index $labels 0 }}

+ {{ range (index $sectionMetadata "source") }} +
+ + +
+ {{ end }} +
+
+

{{ index $labels 1 }}

+ {{ range (index $sectionMetadata "implementation") }} +
+ + +
+ {{ end }} +
+
+

{{ index $labels 2 }}

+ {{ range (index $sectionMetadata "provisionerType") }} +
+ + +
+ {{ end }} +
+
+

{{ index $labels 3 }}

+ {{ range (index $sectionMetadata "resourceType") }} +
+ + +
+ {{ end }} +
+
+

{{ index $labels 4 }}

+ {{ range (index $sectionMetadata "flavor") }} +
+ + +
+ {{ end }} +
+
+

{{ index $labels 5 }}

+ {{ range (index $sectionMetadata "tool") }} +
+ + +
+ {{ end }} +
+ {{ end }} +
+ {{ end }} \ No newline at end of file diff --git a/layouts/shortcodes/example-file.html b/layouts/shortcodes/example-file.html index 9aa8fe1a..e7b42eb8 100644 --- a/layouts/shortcodes/example-file.html +++ b/layouts/shortcodes/example-file.html @@ -34,6 +34,26 @@ "currentPage" $.Page ) -}} {{ end }} + {{ if eq (index (split (.Get "dir") "/") 0) "resource-provisioners" }} + {{ $updatedDir := delimit (after 2 (split (.Get "dir") "/")) "/" }} + {{ $updatedFolder := print "/" $updatedDir "/" (.Get "filename") }} + {{ if eq (index (split (.Get "dir") "/") 1) "default" }} + {{- partial "content/code-block" ( dict + "file" $folder + "gitHubUrl" (.Get "githubUrl" | default $exampleLibraryGitHubBaseUrl) + "currentPage" $.Page + ) -}} + {{ else }} + {{- partial "content/code-block" ( dict + "file" $folder + "gitHubUrl" (print + (.Get "githubUrl" | default $exampleLibraryGitHubBaseUrl) + ( strings.TrimPrefix $exampleLibrarySourcePath $updatedFolder ) + ) + "currentPage" $.Page + ) -}} + {{ end }} + {{ end }} {{ else }} {{ $dirSegments := split (.Get "dir") "/" }} {{ $filteredSegments := slice }} diff --git a/layouts/shortcodes/resource-provisioner-content.html b/layouts/shortcodes/resource-provisioner-content.html new file mode 100644 index 00000000..1646a9b6 --- /dev/null +++ b/layouts/shortcodes/resource-provisioner-content.html @@ -0,0 +1,15 @@ +
{{ .Get "description" }}
+
+type: {{ .Get "type" }}
+{{- if .Get "supportedParams" }}
+supported_params:
+{{- range split (.Get "supportedParams") "," }}
+  - {{ trim . " " }}
+{{- end }}
+{{- end }}
+{{- if .Get "expectedOutputs" }}
+expected_outputs:
+{{- range split (.Get "expectedOutputs") "," }}
+  - {{ trim . " " }}
+{{- end }}
+{{- end }}
\ No newline at end of file diff --git a/package.json b/package.json index eab3f286..35eea350 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,15 @@ "fmt": "dprint fmt", "lint": "vale content/en/docs/**/* && dprint check", "gen-all": "yarn gen-get-external-content && yarn gen-example-pages", - "gen-get-external-content": "yarn gen-score-specification && yarn gen-score-resources-default-provisioners && yarn gen-score-resources-community-provisioners && yarn gen-patch-templates", + "gen-get-external-content": "yarn gen-score-specification && yarn gen-score-resources-default-provisioners && yarn gen-score-resources-community-provisioners && yarn gen-resource-provisioners-community && yarn gen-patch-templates && yarn gen-resource-provisioners-default-score-compose && yarn gen-resource-provisioners-default-score-k8s && yarn gen-transform-default-resource-provisioners", "gen-score-specification": "git rm -rfq gen/external-content/score/specification && git read-tree --prefix=gen/external-content/score/specification -u examples/main:specification", "gen-score-resources-default-provisioners": "git rm -rfq gen/external-content/score/resources/default-provisioners && git read-tree --prefix=gen/external-content/score/resources/default-provisioners -u examples/main:resources", "gen-score-resources-community-provisioners": "git rm -rfq gen/external-content/score/resources/community-provisioners && git read-tree --prefix=gen/external-content/score/resources/community-provisioners -u community-provisioners/main", + "gen-resource-provisioners-community": "git rm -rfq gen/external-content/resource-provisioners/community && git read-tree --prefix=gen/external-content/resource-provisioners/community -u community-provisioners/main", "gen-patch-templates": "git rm -rfq gen/external-content/patch-templates && git read-tree --prefix=gen/external-content/patch-templates -u patch-templates/main", + "gen-resource-provisioners-default-score-compose": "git rm -rfq gen/external-content/resource-provisioners/default/score-compose && git read-tree --prefix=gen/external-content/resource-provisioners/default/score-compose -u score-compose/main:internal/command", + "gen-resource-provisioners-default-score-k8s": "git rm -rfq gen/external-content/resource-provisioners/default/score-k8s && git read-tree --prefix=gen/external-content/resource-provisioners/default/score-k8s -u score-k8s/main:internal/provisioners/default", + "gen-transform-default-resource-provisioners": "node ./gen/examples-site/transform-default-resource-provisioners.js", "gen-example-pages": "node ./gen/examples-site/gen-example-pages.js ./gen/external-content && yarn fmt" }, "version": "0.0.1"