Repository navigation
First draft of user-guide for compositions #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Compositions | ||
|
|
||
| * [Overview](overview.md) | ||
| * [Installation](installation.md) | ||
| * [Writing your first composition](author_first_composition.md) | ||
| * [Writing your first GCP composition](author_first_gcp_composition.md) | ||
| * Test Scenarios: | ||
| * [GCP CloudSQL](gcp_cloudsql_scenario.md) | ||
| * [GCP App Team](gcp_appteam_scenario.md) | ||
| * [AWS EKS cluster](aws_eks_scenario.md) | ||
| * [Azure AKS cluster](azure_aks_scenario.md) | ||
| * [Composition authoring walkthrough](authoring_walkthrough.md) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,297 @@ | ||
| # Writing your first composition | ||
|
|
||
| This document shows you how to write a simple composition that manages K8s-native resources such as a Deployment and ConfigMap. | ||
|
|
||
| ## Concepts | ||
|
|
||
| The concepts explored in this chapter are: | ||
|
|
||
| * Composition | ||
| * Stages | ||
| * Expander types | ||
| * Facade | ||
|
|
||
| ## Your First Composition | ||
|
|
||
| We will start with a simple Composition that deploys a web-server and a landing page that lists the members of a team. | ||
|
|
||
| Typically to achieve this a team would need to deploy: | ||
|
|
||
| 1. Web-server Pod | ||
| 2. Service connecting to the pod | ||
| 3. Configmap that creates a static web page in the web-server pod | ||
|
|
||
| Using Compositions, the same team would need to create a single object that looks something like this: | ||
|
|
||
| ```yaml | ||
| apiVersion: idp.mycompany.com/v1alpha1 | ||
| kind: TeamPage | ||
| metadata: | ||
| name: teampage | ||
| namespace: team-sales | ||
| spec: | ||
| members: | ||
| - name: Jo | ||
| role: Eng Manager | ||
| - name: Jane | ||
| role: Lead | ||
| - name: Bob | ||
| role: Developer | ||
| ``` | ||
|
|
||
| Such an object that abstracts away the underlying complexity and provides the input to a Composition is called a Facade. A Facade is a CRD that captures the inputs from the user which are then used in the Composition. | ||
|
|
||
| ## Composition | ||
|
|
||
| Let’s define a Composition that uses the Facade and creates a web-server Deployment, Service and a Configmap. We shall use `jinja2` based templating in this example. | ||
|
|
||
| A preview of such a [composition](https://github.com/cloud-native-compositions/compositions/tree/main/samples/FirstComposition) looks like this: | ||
|
|
||
| ```yaml | ||
| apiVersion: composition.google.com/v1alpha1 | ||
| kind: Composition | ||
| metadata: | ||
| name: team-page | ||
| spec: | ||
| # we have a plan to replace inputAPIGroup field with apiVersion, Kind fields. | ||
| # inputAPI: | ||
| # apiVersion: idp.mycompany.com/v1alpha1 | ||
| # kind: TeamPage | ||
| inputAPIGroup: teampages.idp.mycompany.com # Facade API | ||
| expanders: | ||
| - type: jinja2 # pluggable expander | ||
| name: server # stage | ||
| template: | # jinja2 template | ||
| apiVersion: apps/v1 | ||
| kind: Deployment | ||
| metadata: | ||
| # Use the teampages Facade's name | ||
| name: team-{{ teampages.metadata.name }} | ||
| # The namespace is set to the facade's namespace | ||
| namespace: default | ||
| labels: | ||
| # use facade's name in the label | ||
| app: nginx-{{ teampages.metadata.name }} | ||
| spec: | ||
| replicas: 1 | ||
| selector: | ||
| matchLabels: | ||
| app: nginx-{{ teampages.metadata.name }} | ||
| template: | ||
| metadata: | ||
| labels: | ||
| # use facade name in the pod's label | ||
| app: nginx-{{ teampages.metadata.name }} | ||
| spec: | ||
| containers: | ||
| - name: server | ||
| image: nginx:1.16.0 | ||
| ports: | ||
| - name: http | ||
| containerPort: 80 | ||
| protocol: TCP | ||
| volumeMounts: | ||
| - name: index | ||
| mountPath: /usr/share/nginx/html/ | ||
| volumes: | ||
| - name: index | ||
| configMap: | ||
| # use the configmap created by this composition | ||
| name: team-{{ teampages.metadata.name }}-page | ||
| --- | ||
| apiVersion: v1 | ||
| kind: Service | ||
| metadata: | ||
| # include the facade name in the service name | ||
| name: team-{{ teampages.metadata.name }}-landing | ||
| namespace: default | ||
| labels: | ||
| app: nginx-{{ teampages.metadata.name }} | ||
| spec: | ||
| ports: | ||
| - port: 80 | ||
| protocol: TCP | ||
| selector: | ||
| # match the web-server pod | ||
| app: nginx-{{ teampages.metadata.name }} | ||
| --- | ||
| apiVersion: v1 | ||
| kind: ConfigMap | ||
| metadata: | ||
| name: team-{{ teampages.metadata.name }}-page | ||
| namespace: default | ||
| data: | ||
| index.html: | | ||
| <html> | ||
| <h1>{{ teampages.metadata.name }}</h1> | ||
| <table> | ||
| <tr> | ||
| <th>Name</th> | ||
| <th>Role</th> | ||
| </tr> | ||
| {% for member in teampages.spec.apps %} | ||
| <tr> | ||
| <td>{{ member.name }}</td> | ||
| <td>{{ member.role }}</td> | ||
| </tr> | ||
| {% endfor %} | ||
| </table> | ||
| </html> | ||
| ``` | ||
|
|
||
| The `team-page` Composition | ||
|
|
||
| * Uses `teampages.idp.mycompany.com` CRD as the input schema | ||
| * Has single stage with 3 resources | ||
| * Uses expander of type `jinja2` | ||
|
|
||
| Please note that the templating language is [pluggable](https://github.com/cloud-native-compositions/compositions/tree/main/expanders/helm-expander). The Composition is split into expander stages. Each expander is evaluated and applied before the next stage is processed. This introduces implicit sequencing. In this example, we will create all the objects in a single expander stage. | ||
|
|
||
| ## Creating Input Schema using kubebuilder | ||
|
|
||
| The composition uses `TeamPage` CRD as its Facade (input schema). The facade CRD can be created using [kubebuilder](https://book.kubebuilder.io/cronjob-tutorial/new-api). Refer [Composition authoring (Step 3: Write the Facade API)](authoring_walkthrough.md) for steps to use kubebuilder to create a Facade CRD. | ||
|
|
||
| Create scaffolding for the `TeamPage` CRD | ||
|
|
||
| ```shell | ||
| kubebuilder create api --group idp --version v1alpha1 --kind TeamPage --controller=false --resource | ||
| ``` | ||
|
|
||
| Edit the golang file to add the `spec` fields | ||
|
|
||
| ```shell | ||
| # choose editor of your choice | ||
| vim api/v1alpha1/teampage_types.go | ||
| ``` | ||
|
|
||
| Edit the `Spec` struct to include the team members: | ||
|
|
||
| ```c | ||
| ## Edit the Spec struct to include the team members | ||
| type Member struct { | ||
| Name string `json:"name"` | ||
| Role string `json:"role"` | ||
| } | ||
|
|
||
| ## The TeamPageSpec should look like this | ||
| type TeamPageSpec struct { | ||
| Members []Member `json:"members"` | ||
| } | ||
| ``` | ||
|
|
||
| The Generated CRD should look like this: | ||
|
|
||
| ```yaml | ||
| apiVersion: apiextensions.k8s.io/v1 | ||
| kind: CustomResourceDefinition | ||
| metadata: | ||
| ... | ||
| name: teampages.idp.mycompany.com | ||
| spec: | ||
| ... | ||
| kind: TeamPage | ||
| ... | ||
| schema: | ||
| ... | ||
| ``` | ||
|
|
||
| ## Apply the composition | ||
|
|
||
| Clone the [repo](https://github.com/cloud-native-compositions/compositions) to get the samples. Apply the `composition/teampage.yaml` to create the Composition and its corresponding facade CRD. | ||
|
|
||
| ```shell | ||
| # if not cloned already | ||
| git clone https://github.com/cloud-native-compositions/compositions.git | ||
|
|
||
| cd samples/FirstComposition | ||
| kubectl apply -f composition/teampage.yaml | ||
| ``` | ||
|
|
||
| Check if the composition is installed successfully. You should see something like this. | ||
|
|
||
| ```shell | ||
| ❯ kubectl get composition team-page -o json | jq ".status" | ||
| { | ||
| "stages": { | ||
| "server": { | ||
| "reason": "ValidationPassed", | ||
| "validationStatus": "success" | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Using the composition | ||
|
|
||
| Once the Composition and the Facade which specifies the input schema are installed, the composition is ready for use. A team in your org creates an instance of the `TeamPage` Facade as an input to the composition. | ||
|
|
||
| The first step is to create a namespace for the team. This namespace is where the `TeamPage` would be created. | ||
|
|
||
| ```shell | ||
| export NAMESPACE=my-team | ||
| kubectl create namespace ${NAMESPACE?} | ||
| ``` | ||
|
|
||
| Next we create a `TeamPage` instance in the namespace: | ||
|
|
||
| ```shell | ||
| kubectl apply -f - <<EOF | ||
| apiVersion: idp.mycompany.com/v1alpha1 | ||
| kind: TeamPage | ||
| metadata: | ||
| name: landing | ||
| namespace: ${NAMESPACE?} | ||
| spec: | ||
| members: | ||
| - name: Jo | ||
| role: Eng Manager | ||
| - name: Jane | ||
| role: Lead | ||
| - name: Bob | ||
| role: Developer | ||
| EOF | ||
| ``` | ||
|
|
||
| ## Verify | ||
|
|
||
| Verify the k8s objects are created successfully: | ||
|
|
||
| ```shell | ||
| kubectl get deployment -n ${NAMESPACE?} | ||
| kubectl get service -n ${NAMESPACE?} | ||
| kubectl get configmap -n ${NAMESPACE?} | ||
| ``` | ||
|
|
||
| Check the web server: | ||
|
|
||
| ```shell | ||
| kubectl port-forward service/team-landing-landing -n my-team 5555:80 | ||
|
|
||
| # seperate terminal, you should see the web page being served correctly | ||
| ❯ curl localhost:5555 | ||
| <html> | ||
| <h1>landing</h1> | ||
| <table> | ||
| <tr> | ||
| <th>Name</th> | ||
| <th>Role</th> | ||
| </tr> | ||
|
|
||
| <tr> | ||
| <td>Jo</td> | ||
| <td>Eng Manager</td> | ||
| </tr> | ||
|
|
||
| <tr> | ||
| <td>Jane</td> | ||
| <td>Lead</td> | ||
| </tr> | ||
|
|
||
| <tr> | ||
| <td>Bob</td> | ||
| <td>Developer</td> | ||
| </tr> | ||
|
|
||
| </table> | ||
| </html> | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.