From 68e55ca5bf0899475d104a46a38dab706edad3b5 Mon Sep 17 00:00:00 2001 From: Justin Ross Date: Fri, 2 Feb 2024 13:24:48 -0500 Subject: [PATCH] Skewer update --- .github/workflows/main.yaml | 2 +- README.md | 165 ++++------ east/site.yaml | 2 +- .../skewer-main/example/external/skewer-main | 1 - external/skewer-main/example/python/plano | 1 - external/skewer-main/example/python/skewer | 1 - external/skewer-main/python/plano | 1 - .../python/skewer/standardsteps.yaml | 278 ----------------- .../.github/workflows/main.yaml | 0 external/{skewer-main => skewer}/.gitignore | 0 external/{skewer-main => skewer}/.plano.py | 27 +- external/{skewer-main => skewer}/LICENSE.txt | 0 external/{skewer-main => skewer}/README.md | 70 +++-- .../config/.github/workflows/main.yaml | 2 +- .../{skewer-main => skewer}/config/.gitignore | 0 .../{skewer-main => skewer}/config/.plano.py | 0 .../example/.gitignore | 0 .../{skewer-main => skewer}/example/.plano.py | 0 .../{skewer-main => skewer}/example/README.md | 249 ++++++--------- .../{skewer-main => skewer}/example/plano | 0 external/skewer/example/python/plano | 1 + external/skewer/example/python/skewer | 1 + .../example/skewer.yaml | 16 +- .../plano}/.github/workflows/main.yaml | 0 .../external/plano}/.gitignore | 0 .../external/plano}/LICENSE.txt | 0 .../external/plano}/MANIFEST.in | 0 .../external/plano}/Makefile | 0 .../external/plano}/README.md | 18 +- .../external/plano}/bin/plano | 0 .../external/plano}/bin/plano-test | 0 .../external/plano}/docs/conf.py | 0 .../external/plano}/docs/index.rst | 0 .../external/plano}/pyproject.toml | 0 .../external/plano}/src/plano/__init__.py | 0 .../plano}/src/plano/_testproject/.plano.py | 2 +- .../_testproject/src/chucker/__init__.py | 0 .../_testproject/src/chucker/moretests.py | 24 ++ .../plano/_testproject/src/chucker/tests.py | 11 + .../external/plano}/src/plano/_tests.py | 48 ++- .../external/plano}/src/plano/command.py | 27 +- .../skewer/external/plano/src/plano/github.py | 78 +++++ .../external/plano}/src/plano/main.py | 86 +++--- .../external/plano}/src/plano/test.py | 16 +- external/{skewer-main => skewer}/plano | 0 external/skewer/python/plano | 1 + .../python/skewer/__init__.py | 0 .../python/skewer/main.py | 26 +- .../python/skewer/planocommands.py | 48 +-- .../skewer/python/skewer/standardsteps.yaml | 292 ++++++++++++++++++ .../python/skewer/standardtext.yaml | 0 .../python/skewer/tests.py | 0 python/plano | 2 +- python/skewer | 2 +- skewer.yaml | 16 +- west/site.yaml | 2 - 56 files changed, 783 insertions(+), 733 deletions(-) delete mode 120000 external/skewer-main/example/external/skewer-main delete mode 120000 external/skewer-main/example/python/plano delete mode 120000 external/skewer-main/example/python/skewer delete mode 120000 external/skewer-main/python/plano delete mode 100644 external/skewer-main/python/skewer/standardsteps.yaml rename external/{skewer-main => skewer}/.github/workflows/main.yaml (100%) rename external/{skewer-main => skewer}/.gitignore (100%) rename external/{skewer-main => skewer}/.plano.py (74%) rename external/{skewer-main => skewer}/LICENSE.txt (100%) rename external/{skewer-main => skewer}/README.md (81%) rename external/{skewer-main => skewer}/config/.github/workflows/main.yaml (99%) rename external/{skewer-main => skewer}/config/.gitignore (100%) rename external/{skewer-main => skewer}/config/.plano.py (100%) rename external/{skewer-main => skewer}/example/.gitignore (100%) rename external/{skewer-main => skewer}/example/.plano.py (100%) rename external/{skewer-main => skewer}/example/README.md (52%) rename external/{skewer-main => skewer}/example/plano (100%) create mode 120000 external/skewer/example/python/plano create mode 120000 external/skewer/example/python/skewer rename external/{skewer-main => skewer}/example/skewer.yaml (61%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/.github/workflows/main.yaml (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/.gitignore (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/LICENSE.txt (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/MANIFEST.in (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/Makefile (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/README.md (77%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/bin/plano (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/bin/plano-test (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/docs/conf.py (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/docs/index.rst (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/pyproject.toml (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/__init__.py (100%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/_testproject/.plano.py (97%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/_testproject/src/chucker/__init__.py (100%) create mode 100644 external/skewer/external/plano/src/plano/_testproject/src/chucker/moretests.py rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/_testproject/src/chucker/tests.py (86%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/_tests.py (96%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/command.py (95%) create mode 100644 external/skewer/external/plano/src/plano/github.py rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/main.py (95%) rename external/{skewer-main/external/plano-main => skewer/external/plano}/src/plano/test.py (96%) rename external/{skewer-main => skewer}/plano (100%) create mode 120000 external/skewer/python/plano rename external/{skewer-main => skewer}/python/skewer/__init__.py (100%) rename external/{skewer-main => skewer}/python/skewer/main.py (96%) rename external/{skewer-main => skewer}/python/skewer/planocommands.py (64%) create mode 100644 external/skewer/python/skewer/standardsteps.yaml rename external/{skewer-main => skewer}/python/skewer/standardtext.yaml (100%) rename external/{skewer-main => skewer}/python/skewer/tests.py (100%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index cd492b1..db00f3c 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -24,7 +24,7 @@ on: schedule: - cron: "0 0 * * 0" jobs: - main: + test: strategy: fail-fast: false matrix: diff --git a/README.md b/README.md index 0b651b8..3941ff5 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,11 @@ across cloud providers, data centers, and edge sites. * [Overview](#overview) * [Prerequisites](#prerequisites) -* [Step 1: Configure separate console sessions](#step-1-configure-separate-console-sessions) -* [Step 2: Access your clusters](#step-2-access-your-clusters) -* [Step 3: Install Skupper in your clusters](#step-3-install-skupper-in-your-clusters) -* [Step 4: Set up your namespaces](#step-4-set-up-your-namespaces) -* [Step 5: Apply your YAML resources](#step-5-apply-your-yaml-resources) -* [Step 6: Link your namespaces](#step-6-link-your-namespaces) -* [Step 7: Test the application](#step-7-test-the-application) -* [Accessing the web console](#accessing-the-web-console) +* [Step 1: Install Skupper in your clusters](#step-1-install-skupper-in-your-clusters) +* [Step 2: Set up your namespaces](#step-2-set-up-your-namespaces) +* [Step 3: Apply your YAML resources](#step-3-apply-your-yaml-resources) +* [Step 4: Link your sites](#step-4-link-your-sites) +* [Step 5: Access the frontend](#step-5-access-the-frontend) * [Cleaning up](#cleaning-up) * [Summary](#summary) * [Next steps](#next-steps) @@ -66,54 +63,12 @@ services without exposing the backend to the public internet. [install-kubectl]: https://kubernetes.io/docs/tasks/tools/install-kubectl/ [kube-providers]: https://skupper.io/start/kubernetes.html -## Step 1: Configure separate console sessions - -Skupper is designed for use with multiple namespaces, usually on -different clusters. The `skupper` and `kubectl` commands use your -[kubeconfig][kubeconfig] and current context to select the -namespace where they operate. - -[kubeconfig]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ - -Your kubeconfig is stored in a file in your home directory. The -`skupper` and `kubectl` commands use the `KUBECONFIG` environment -variable to locate it. - -A single kubeconfig supports only one active context per user. -Since you will be using multiple contexts at once in this -exercise, you need to create distinct kubeconfigs. - -Start a console session for each of your namespaces. Set the -`KUBECONFIG` environment variable to a different path in each -session. - -_**Console for West:**_ - -~~~ shell -export KUBECONFIG=~/.kube/config-west -~~~ - -_**Console for East:**_ - -~~~ shell -export KUBECONFIG=~/.kube/config-east -~~~ - -## Step 2: Access your clusters - -The procedure for accessing a Kubernetes cluster varies by -provider. [Find the instructions for your chosen -provider][kube-providers] and use them to authenticate and -configure access for each console session. - -[kube-providers]: https://skupper.io/start/kubernetes.html - -## Step 3: Install Skupper in your clusters +## Step 1: Install Skupper in your clusters Use the `kubectl apply` command to install the Skupper controller in each cluster. -_**Console for West:**_ +_**West:**_ ~~~ shell kubectl apply -f skupper.yaml @@ -130,7 +85,7 @@ clusterrolebinding.rbac.authorization.k8s.io/skupper-site-controller created deployment.apps/skupper-site-controller created ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell kubectl apply -f skupper.yaml @@ -147,27 +102,57 @@ clusterrolebinding.rbac.authorization.k8s.io/skupper-site-controller created deployment.apps/skupper-site-controller created ~~~ -## Step 4: Set up your namespaces +## Step 2: Set up your namespaces + +Skupper is designed for use with multiple Kubernetes namespaces, +usually on different clusters. The `skupper` and `kubectl` +commands use your [kubeconfig][kubeconfig] and current context to +select the namespace where they operate. -Use `kubectl create namespace` to create the namespaces you wish -to use (or use existing namespaces). Use `kubectl config -set-context` to set the current namespace for each session. +[kubeconfig]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ + +Your kubeconfig is stored in a file in your home directory. The +`skupper` and `kubectl` commands use the `KUBECONFIG` environment +variable to locate it. + +A single kubeconfig supports only one active context per user. +Since you will be using multiple contexts at once in this +exercise, you need to create distinct kubeconfigs. + +For each namespace, open a new terminal window. In each terminal, +set the `KUBECONFIG` environment variable to a different path and +log in to your cluster. Then create the namespace you wish to use +and set the namespace on your current context. + +**Note:** The login procedure varies by provider. See the +documentation for yours: -_**Console for West:**_ +* [Minikube](https://skupper.io/start/minikube.html#cluster-access) +* [Amazon Elastic Kubernetes Service (EKS)](https://skupper.io/start/eks.html#cluster-access) +* [Azure Kubernetes Service (AKS)](https://skupper.io/start/aks.html#cluster-access) +* [Google Kubernetes Engine (GKE)](https://skupper.io/start/gke.html#cluster-access) +* [IBM Kubernetes Service](https://skupper.io/start/ibmks.html#cluster-access) +* [OpenShift](https://skupper.io/start/openshift.html#cluster-access) + +_**West:**_ ~~~ shell +export KUBECONFIG=~/.kube/config-west +# Enter your provider-specific login command kubectl create namespace west kubectl config set-context --current --namespace west ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell +export KUBECONFIG=~/.kube/config-east +# Enter your provider-specific login command kubectl create namespace east kubectl config set-context --current --namespace east ~~~ -## Step 5: Apply your YAML resources +## Step 3: Apply your YAML resources To configure our example sites and service bindings, we are using the following resources: @@ -266,7 +251,7 @@ Skupper sites. [minikube-tunnel]: https://skupper.io/start/minikube.html#running-minikube-tunnel -_**Console for West:**_ +_**West:**_ ~~~ shell kubectl apply -f west/site.yaml -f west/frontend.yaml @@ -278,10 +263,9 @@ _Sample output:_ $ kubectl apply -f west/site.yaml -f west/frontend.yaml configmap/skupper-site created deployment.apps/frontend created -service/frontend created ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell kubectl apply -f east/site.yaml -f east/backend.yaml @@ -295,7 +279,7 @@ configmap/skupper-site created deployment.apps/backend created ~~~ -## Step 6: Link your namespaces +## Step 4: Link your sites You can configure sites and service bindings declaratively, but linking sites is different. To create a link, you must have the @@ -326,7 +310,7 @@ East to create a link. [install]: https://skupper.io/install/index.html -_**Console for West:**_ +_**West:**_ ~~~ shell skupper token create ~/secret.token @@ -339,7 +323,7 @@ $ skupper token create ~/secret.token Token written to ~/secret.token ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell skupper link create ~/secret.token @@ -353,16 +337,15 @@ Site configured to link to https://10.105.193.154:8081/ed9c37f6-d78a-11ec-a8c7-0 Check the status of the link using 'skupper link status'. ~~~ -If your console sessions are on different machines, you may need +If your terminal sessions are on different machines, you may need to use `scp` or a similar tool to transfer the token securely. By default, tokens expire after a single use or 15 minutes after creation. -## Step 7: Test the application +## Step 5: Access the frontend -We have established connectivity between the two namespaces and -made the backend available to the frontend. Before we can test -the application, we need external access to the frontend. +In order to use and test the application, we need external access +to the frontend. Use `kubectl expose` with `--type LoadBalancer` to open network access to the frontend service. @@ -377,7 +360,7 @@ request the `/api/health` endpoint at that address. **Note:** The `` field in the following commands is a placeholder. The actual value is an IP address. -_**Console for West:**_ +_**West:**_ ~~~ shell kubectl expose deployment/frontend --port 8080 --type LoadBalancer @@ -402,53 +385,19 @@ OK If everything is in order, you can now access the web interface by navigating to `http://:8080/` in your browser. -## Accessing the web console - -Skupper includes a web console you can use to view the application -network. To access it, use `skupper status` to look up the URL of -the web console. Then use `kubectl get -secret/skupper-console-users` to look up the console admin -password. - -**Note:** The `` and `` fields in the -following output are placeholders. The actual values are specific -to your environment. - -_**Console for West:**_ - -~~~ shell -skupper status -kubectl get secret/skupper-console-users -o jsonpath={.data.admin} | base64 -d -~~~ - -_Sample output:_ - -~~~ console -$ skupper status -Skupper is enabled for namespace "west". It is connected to 1 other site. It has 1 exposed service. -The site console url is: -The credentials for internal console-auth mode are held in secret: 'skupper-console-users' - -$ kubectl get secret/skupper-console-users -o jsonpath={.data.admin} | base64 -d - -~~~ - -Navigate to `` in your browser. When prompted, log -in as user `admin` and enter the password. - ## Cleaning up To remove Skupper and the other resources from this exercise, use the following commands. -_**Console for West:**_ +_**West:**_ ~~~ shell kubectl delete -f west/site.yaml -f west/frontend.yaml kubectl delete -f skupper.yaml ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell kubectl delete -f east/site.yaml -f east/backend.yaml diff --git a/east/site.yaml b/east/site.yaml index 0c5dae3..ea40e2b 100644 --- a/east/site.yaml +++ b/east/site.yaml @@ -4,4 +4,4 @@ metadata: name: skupper-site data: name: east - ingress: "none" + ingress: none diff --git a/external/skewer-main/example/external/skewer-main b/external/skewer-main/example/external/skewer-main deleted file mode 120000 index c25bddb..0000000 --- a/external/skewer-main/example/external/skewer-main +++ /dev/null @@ -1 +0,0 @@ -../.. \ No newline at end of file diff --git a/external/skewer-main/example/python/plano b/external/skewer-main/example/python/plano deleted file mode 120000 index a578137..0000000 --- a/external/skewer-main/example/python/plano +++ /dev/null @@ -1 +0,0 @@ -../external/skewer-main/python/plano \ No newline at end of file diff --git a/external/skewer-main/example/python/skewer b/external/skewer-main/example/python/skewer deleted file mode 120000 index 6ea41b9..0000000 --- a/external/skewer-main/example/python/skewer +++ /dev/null @@ -1 +0,0 @@ -../external/skewer-main/python/skewer \ No newline at end of file diff --git a/external/skewer-main/python/plano b/external/skewer-main/python/plano deleted file mode 120000 index a1aa027..0000000 --- a/external/skewer-main/python/plano +++ /dev/null @@ -1 +0,0 @@ -../external/plano-main/src/plano \ No newline at end of file diff --git a/external/skewer-main/python/skewer/standardsteps.yaml b/external/skewer-main/python/skewer/standardsteps.yaml deleted file mode 100644 index d5dbcbf..0000000 --- a/external/skewer-main/python/skewer/standardsteps.yaml +++ /dev/null @@ -1,278 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -install_the_skupper_command_line_tool: - title: Install the Skupper command-line tool - preamble: | - The `skupper` command-line tool is the entrypoint for installing - and configuring Skupper. You need to install the `skupper` - command only once for each development environment. - - On Linux or Mac, you can use the install script (inspect it - [here][install-script]) to download and extract the command: - - ~~~ shell - curl https://skupper.io/install.sh | sh - ~~~ - - The script installs the command under your home directory. It - prompts you to add the command to your path if necessary. - - For Windows and other installation options, see [Installing - Skupper][install-docs]. - - [install-script]: https://github.com/skupperproject/skupper-website/blob/main/input/install.sh - [install-docs]: https://skupper.io/install/ -configure_separate_console_sessions: - title: Configure separate console sessions - preamble: | - Skupper is designed for use with multiple namespaces, usually on - different clusters. The `skupper` and `kubectl` commands use your - [kubeconfig][kubeconfig] and current context to select the - namespace where they operate. - - [kubeconfig]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ - - Your kubeconfig is stored in a file in your home directory. The - `skupper` and `kubectl` commands use the `KUBECONFIG` environment - variable to locate it. - - A single kubeconfig supports only one active context per user. - Since you will be using multiple contexts at once in this - exercise, you need to create distinct kubeconfigs. - - Start a console session for each of your namespaces. Set the - `KUBECONFIG` environment variable to a different path in each - session. - commands: - "*": - - run: export KUBECONFIG=@kubeconfig@ -access_your_clusters: - title: Access your clusters - preamble: | - - The procedure for accessing a Kubernetes cluster varies by - provider. [Find the instructions for your chosen - provider][kube-providers] and use them to authenticate and - configure access for each console session. - - [kube-providers]: https://skupper.io/start/kubernetes.html -set_up_your_namespaces: - title: Set up your namespaces - preamble: | - Use `kubectl create namespace` to create the namespaces you wish - to use (or use existing namespaces). Use `kubectl config - set-context` to set the current namespace for each session. - commands: - "*": - - run: kubectl create namespace @namespace@ - - run: kubectl config set-context --current --namespace @namespace@ -install_skupper_in_your_namespaces: - title: Install Skupper in your namespaces - preamble: | - The `skupper init` command installs the Skupper router and - controller in the current namespace. Run the `skupper init` command - in each namespace. - - **Note:** If you are using Minikube, [you need to start `minikube - tunnel`][minikube-tunnel] before you install Skupper. - - [minikube-tunnel]: https://skupper.io/start/minikube.html#running-minikube-tunnel - commands: - "0": - - run: skupper init --enable-console --enable-flow-collector - "*": - - run: skupper init - postamble: | - _Sample output:_ - - ~~~ console - $ skupper init - Waiting for LoadBalancer IP or hostname... - Waiting for status... - Skupper is now installed in namespace ''. Use 'skupper status' to get more information. - ~~~ -check_the_status_of_your_namespaces: - title: Check the status of your namespaces - preamble: | - Use `skupper status` in each console to check that Skupper is - installed. - commands: - "*": - - run: skupper status - postamble: | - _Sample output:_ - - ~~~ console - $ skupper status - Skupper is enabled for namespace "" in interior mode. It is connected to 1 other site. It has 1 exposed service. - The site console url is: - The credentials for internal console-auth mode are held in secret: 'skupper-console-users' - ~~~ - - As you move through the steps below, you can use `skupper status` at - any time to check your progress. -link_your_namespaces: - title: Link your namespaces - preamble: | - Creating a link requires use of two `skupper` commands in - conjunction, `skupper token create` and `skupper link create`. - - The `skupper token create` command generates a secret token that - signifies permission to create a link. The token also carries the - link details. Then, in a remote namespace, The `skupper link - create` command uses the token to create a link to the namespace - that generated it. - - **Note:** The link token is truly a *secret*. Anyone who has the - token can link to your namespace. Make sure that only those you - trust have access to it. - - First, use `skupper token create` in one namespace to generate the - token. Then, use `skupper link create` in the other to create a - link. - commands: - "0": - - run: skupper token create ~/secret.token - output: Token written to ~/secret.token - "1": - - run: skupper link create ~/secret.token - output: | - Site configured to link to https://10.105.193.154:8081/ed9c37f6-d78a-11ec-a8c7-04421a4c5042 (name=link1) - Check the status of the link using 'skupper link status'. - - run: skupper link status --wait 60 - apply: test - postamble: | - If your console sessions are on different machines, you may need - to use `scp` or a similar tool to transfer the token securely. By - default, tokens expire after a single use or 15 minutes after - creation. -accessing_the_web_console: - title: Accessing the web console - numbered: false - preamble: | - Skupper includes a web console you can use to view the application - network. To access it, use `skupper status` to look up the URL of - the web console. Then use `kubectl get - secret/skupper-console-users` to look up the console admin - password. - - **Note:** The `` and `` fields in the - following output are placeholders. The actual values are specific - to your environment. - commands: - "0": - - run: skupper status - output: | - Skupper is enabled for namespace "@namespace@". It is connected to 1 other site. It has 1 exposed service. - The site console url is: - The credentials for internal console-auth mode are held in secret: 'skupper-console-users' - - await_resource: secret/skupper-console-users - - run: kubectl get secret/skupper-console-users -o jsonpath={.data.admin} | base64 -d - output: - - await_console_ok: - postamble: | - Navigate to `` in your browser. When prompted, log - in as user `admin` and enter the password. -cleaning_up: - name: cleaning_up - title: Cleaning up - numbered: false - preamble: | - To remove Skupper and the other resources from this exercise, use - the following commands. - commands: - "*": - - run: skupper delete -hello_world/deploy_the_application: - title: Deploy the application - preamble: | - Use `kubectl create deployment` to deploy the frontend and backend - services. - commands: - "0": - - run: kubectl create deployment frontend --image quay.io/skupper/hello-world-frontend - "1": - - run: kubectl create deployment backend --image quay.io/skupper/hello-world-backend --replicas 3 -hello_world/expose_the_backend_service: - title: Expose the backend service - preamble: | - We now have two namespaces linked to form a Skupper network, but - no services are exposed on it. Skupper uses the `skupper - expose` command to select a service from one namespace for - exposure in all the linked namespaces. - - Use `skupper expose` to expose the backend service on the Skupper - network. - commands: - "1": - - await_resource: deployment/backend - - run: skupper expose deployment/backend --port 8080 - output: deployment backend exposed as backend -hello_world/test_the_application: - title: Test the application - preamble: | - We have established connectivity between the two namespaces and - made the backend available to the frontend. Before we can test - the application, we need external access to the frontend. - - Use `kubectl expose` with `--type LoadBalancer` to open network - access to the frontend service. - - Once the frontend is exposed, use `kubectl get service/frontend` - to look up the external IP of the frontend service. If the - external IP is ``, try again after a moment. - - Once you have the external IP, use `curl` or a similar tool to - request the `/api/health` endpoint at that address. - - **Note:** The `` field in the following commands is a - placeholder. The actual value is an IP address. - commands: - "0": - - run: kubectl expose deployment/frontend --port 8080 --type LoadBalancer - output: service/frontend exposed - - await_resource: service/frontend - - run: kubectl get service/frontend - apply: readme - output: | - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - frontend LoadBalancer 10.103.232.28 8080:30407/TCP 15s - - run: curl http://:8080/api/health - apply: readme - output: OK - - await_http_ok: [service/frontend, "http://{}:8080/api/health"] - postamble: | - If everything is in order, you can now access the web interface by - navigating to `http://:8080/` in your browser. -hello_world/cleaning_up: - name: cleaning_up - title: Cleaning up - numbered: false - preamble: | - To remove Skupper and the other resources from this exercise, use - the following commands. - commands: - "0": - - run: skupper delete - - run: kubectl delete service/frontend - - run: kubectl delete deployment/frontend - "1": - - run: skupper delete - - run: kubectl delete deployment/backend diff --git a/external/skewer-main/.github/workflows/main.yaml b/external/skewer/.github/workflows/main.yaml similarity index 100% rename from external/skewer-main/.github/workflows/main.yaml rename to external/skewer/.github/workflows/main.yaml diff --git a/external/skewer-main/.gitignore b/external/skewer/.gitignore similarity index 100% rename from external/skewer-main/.gitignore rename to external/skewer/.gitignore diff --git a/external/skewer-main/.plano.py b/external/skewer/.plano.py similarity index 74% rename from external/skewer-main/.plano.py rename to external/skewer/.plano.py index 63feba6..fe427c3 100644 --- a/external/skewer-main/.plano.py +++ b/external/skewer/.plano.py @@ -20,27 +20,15 @@ import skewer.tests from plano import * +from plano.github import * from skewer import * @command(passthrough=True) -def test(verbose=False, quiet=False, passthrough_args=[]): - args = ["-m", "skewer.tests"] - - if verbose: - args.append("--verbose") - - if quiet: - args.append("--quiet") - - args += passthrough_args - - PlanoTestCommand().main(args=args) +def test(passthrough_args=[]): + PlanoTestCommand(skewer.tests).main(args=passthrough_args) @command def coverage(verbose=False, quiet=False): - """ - Run the tests and measure code coverage - """ check_program("coverage") with working_env(PYTHONPATH="python"): @@ -57,9 +45,10 @@ def render(verbose=False, quiet=False): """ Render README.html from README.md """ - check_program("pandoc") + markdown = read("README.md") + html = convert_github_markdown(markdown) - run(f"pandoc -o README.html README.md") + write("README.html", html) if not quiet: print(f"file:{get_real_path('README.html')}") @@ -76,6 +65,4 @@ def update_plano(): """ Update the embedded Plano repo """ - make_dir("external") - remove("external/plano-main") - run("curl -sfL https://github.com/ssorj/plano/archive/main.tar.gz | tar -C external -xz", shell=True) + update_external_from_github("external/plano", "ssorj", "plano") diff --git a/external/skewer-main/LICENSE.txt b/external/skewer/LICENSE.txt similarity index 100% rename from external/skewer-main/LICENSE.txt rename to external/skewer/LICENSE.txt diff --git a/external/skewer-main/README.md b/external/skewer/README.md similarity index 81% rename from external/skewer-main/README.md rename to external/skewer/README.md index 76b30a0..f81793d 100644 --- a/external/skewer-main/README.md +++ b/external/skewer/README.md @@ -25,28 +25,31 @@ Change directory to the root of your example project: cd / -Add the Skewer code as a subdirectory in your example project: +Add the Skewer code as a subdirectory: mkdir -p external curl -sfL https://github.com/skupperproject/skewer/archive/main.tar.gz | tar -C external -xz + mv external/skewer-main external/skewer Symlink the Skewer and Plano libraries into your `python` directory: mkdir -p python - ln -s ../external/skewer-main/python/skewer python/skewer - ln -s ../external/skewer-main/python/plano python/plano + ln -s ../external/skewer/python/skewer python/skewer + ln -s ../external/skewer/python/plano python/plano Copy the `plano` command into the root of your project: - cp external/skewer-main/plano plano + cp external/skewer/plano plano -Copy the standard config files and workflow file into your project: +Copy the standard config files: - cp external/skewer-main/config/.plano.py .plano.py - cp external/skewer-main/config/.gitignore .gitignore + cp external/skewer/config/.plano.py .plano.py + cp external/skewer/config/.gitignore .gitignore + +Copy the standard workflow file: mkdir -p .github/workflows - cp external/skewer-main/config/.github/workflows/main.yaml .github/workflows/main.yaml + cp external/skewer/config/.github/workflows/main.yaml .github/workflows/main.yaml Use your editor to create a `skewer.yaml` file in the root of your project: @@ -90,7 +93,7 @@ The top level: ~~~ yaml title: # Your example's title (required) -subtitle: # Your chosen subtitle (required) +subtitle: # Your chosen subtitle (optional) workflow: # The filename of your GitHub workflow (optional, default 'main.yaml') overview: # Text introducing your example (optional) prerequisites: # Text describing prerequisites (optional, has default text) @@ -100,6 +103,8 @@ summary: # Text to summarize what the user did (optional) next_steps: # Text linking to more examples (optional, has default text) ~~~ +To disable the GitHub workflow, set it to `null`. + A **site**: ~~~ yaml @@ -186,37 +191,42 @@ to `standard`: - run: skupper delete ~~~ -The initial steps are usually standard ones. There are also some -standard steps at the end. You may be able to use something like -this: +A typical mix of standard and custom steps might look like this: ~~~ yaml steps: - - standard: configure_separate_console_sessions - - standard: access_your_clusters - - standard: set_up_your_namespaces - - standard: install_skupper_in_your_namespaces - - standard: check_the_status_of_your_namespaces - - standard: link_your_namespaces - - - standard: accessing_the_web_console + - standard: install_the_skupper_command_line_tool + - standard: kubernetes/set_up_your_namespaces + + - standard: kubernetes/create_your_sites + - standard: link_your_sites + + - standard: cleaning_up ~~~ -Note that the `link_your_namespaces` step is less generic than the -other steps (it assumes only two sites), so check that the text and -commands it produces are doing what you need. If not, you'll need to -provide a custom step. +**Note:** The `link_your_sites`, `access_the_application`, and +`cleaning_up` steps are less generic than the other steps. +`link_your_sites` assumes just two sites. `access_the_application` +assumes you have a `frontend` service. `cleaning_up` doesn't delete +any application workoads. Check that the text and commands these +steps produce are doing what you need for your example. If not, you +need to provide a custom step. -There are also some standard steps for examples based on the Skupper +There are some standard steps for examples based on the Skupper Hello World application: ~~~ yaml -steps: - - standard: hello_world/deploy_the_application - - standard: hello_world/expose_the_backend_service - - standard: hello_world/test_the_application - - standard: hello_world/cleaning_up +- standard: hello_world/deploy_the_frontend_and_backend +- standard: hello_world/expose_the_backend +- standard: hello_world/access_the_frontend +- standard: hello_world/cleaning_up +~~~ + +And finally there are some special cases: +~~~ yaml +- standard: kubernetes/set_up_your_kubernetes_namespace +- standard: podman/set_up_your_podman_network ~~~ The step commands are separated into named groups corresponding to the diff --git a/external/skewer-main/config/.github/workflows/main.yaml b/external/skewer/config/.github/workflows/main.yaml similarity index 99% rename from external/skewer-main/config/.github/workflows/main.yaml rename to external/skewer/config/.github/workflows/main.yaml index cd492b1..db00f3c 100644 --- a/external/skewer-main/config/.github/workflows/main.yaml +++ b/external/skewer/config/.github/workflows/main.yaml @@ -24,7 +24,7 @@ on: schedule: - cron: "0 0 * * 0" jobs: - main: + test: strategy: fail-fast: false matrix: diff --git a/external/skewer-main/config/.gitignore b/external/skewer/config/.gitignore similarity index 100% rename from external/skewer-main/config/.gitignore rename to external/skewer/config/.gitignore diff --git a/external/skewer-main/config/.plano.py b/external/skewer/config/.plano.py similarity index 100% rename from external/skewer-main/config/.plano.py rename to external/skewer/config/.plano.py diff --git a/external/skewer-main/example/.gitignore b/external/skewer/example/.gitignore similarity index 100% rename from external/skewer-main/example/.gitignore rename to external/skewer/example/.gitignore diff --git a/external/skewer-main/example/.plano.py b/external/skewer/example/.plano.py similarity index 100% rename from external/skewer-main/example/.plano.py rename to external/skewer/example/.plano.py diff --git a/external/skewer-main/example/README.md b/external/skewer/example/README.md similarity index 52% rename from external/skewer-main/example/README.md rename to external/skewer/example/README.md index 39b2ac4..aed9d8d 100644 --- a/external/skewer-main/example/README.md +++ b/external/skewer/example/README.md @@ -16,17 +16,13 @@ across cloud providers, data centers, and edge sites. * [Overview](#overview) * [Prerequisites](#prerequisites) * [Step 1: Install the Skupper command-line tool](#step-1-install-the-skupper-command-line-tool) -* [Step 2: Configure separate console sessions](#step-2-configure-separate-console-sessions) -* [Step 3: Access your clusters](#step-3-access-your-clusters) -* [Step 4: Set up your namespaces](#step-4-set-up-your-namespaces) -* [Step 5: Install Skupper in your namespaces](#step-5-install-skupper-in-your-namespaces) -* [Step 6: Check the status of your namespaces](#step-6-check-the-status-of-your-namespaces) -* [Step 7: Link your namespaces](#step-7-link-your-namespaces) -* [Step 8: Fail on demand](#step-8-fail-on-demand) -* [Step 9: Deploy the application](#step-9-deploy-the-application) -* [Step 10: Expose the backend service](#step-10-expose-the-backend-service) -* [Step 11: Test the application](#step-11-test-the-application) -* [Accessing the web console](#accessing-the-web-console) +* [Step 2: Set up your namespaces](#step-2-set-up-your-namespaces) +* [Step 3: Deploy the frontent and backend](#step-3-deploy-the-frontent-and-backend) +* [Step 4: Create your sites](#step-4-create-your-sites) +* [Step 5: Link your sites](#step-5-link-your-sites) +* [Step 6: Fail on demand](#step-6-fail-on-demand) +* [Step 7: Expose the backend](#step-7-expose-the-backend) +* [Step 8: Access the frontend](#step-8-access-the-frontend) * [Cleaning up](#cleaning-up) * [Summary](#summary) * [Next steps](#next-steps) @@ -42,9 +38,9 @@ Some prerequisites ## Step 1: Install the Skupper command-line tool -The `skupper` command-line tool is the entrypoint for installing -and configuring Skupper. You need to install the `skupper` -command only once for each development environment. +This example uses the Skupper command-line tool to deploy Skupper. +You need to install the `skupper` command only once for each +development environment. On Linux or Mac, you can use the install script (inspect it [here][install-script]) to download and extract the command: @@ -62,12 +58,12 @@ Skupper][install-docs]. [install-script]: https://github.com/skupperproject/skupper-website/blob/main/input/install.sh [install-docs]: https://skupper.io/install/ -## Step 2: Configure separate console sessions +## Step 2: Set up your namespaces -Skupper is designed for use with multiple namespaces, usually on -different clusters. The `skupper` and `kubectl` commands use your -[kubeconfig][kubeconfig] and current context to select the -namespace where they operate. +Skupper is designed for use with multiple Kubernetes namespaces, +usually on different clusters. The `skupper` and `kubectl` +commands use your [kubeconfig][kubeconfig] and current context to +select the namespace where they operate. [kubeconfig]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ @@ -79,72 +75,81 @@ A single kubeconfig supports only one active context per user. Since you will be using multiple contexts at once in this exercise, you need to create distinct kubeconfigs. -Start a console session for each of your namespaces. Set the -`KUBECONFIG` environment variable to a different path in each -session. +For each namespace, open a new terminal window. In each terminal, +set the `KUBECONFIG` environment variable to a different path and +log in to your cluster. Then create the namespace you wish to use +and set the namespace on your current context. -_**Console for West:**_ +**Note:** The login procedure varies by provider. See the +documentation for yours: + +* [Minikube](https://skupper.io/start/minikube.html#cluster-access) +* [Amazon Elastic Kubernetes Service (EKS)](https://skupper.io/start/eks.html#cluster-access) +* [Azure Kubernetes Service (AKS)](https://skupper.io/start/aks.html#cluster-access) +* [Google Kubernetes Engine (GKE)](https://skupper.io/start/gke.html#cluster-access) +* [IBM Kubernetes Service](https://skupper.io/start/ibmks.html#cluster-access) +* [OpenShift](https://skupper.io/start/openshift.html#cluster-access) + +_**West:**_ ~~~ shell export KUBECONFIG=~/.kube/config-west +# Enter your provider-specific login command +kubectl create namespace west +kubectl config set-context --current --namespace west ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell export KUBECONFIG=~/.kube/config-east +# Enter your provider-specific login command +kubectl create namespace east +kubectl config set-context --current --namespace east ~~~ -## Step 3: Access your clusters - -The procedure for accessing a Kubernetes cluster varies by -provider. [Find the instructions for your chosen -provider][kube-providers] and use them to authenticate and -configure access for each console session. - -[kube-providers]: https://skupper.io/start/kubernetes.html +## Step 3: Deploy the frontent and backend -## Step 4: Set up your namespaces +This example runs the frontend and the backend in separate +Kubernetes namespaces, on different clusters. -Use `kubectl create namespace` to create the namespaces you wish -to use (or use existing namespaces). Use `kubectl config -set-context` to set the current namespace for each session. +Use `kubectl create deployment` to deploy the frontend in +namespace `west` and the backend in namespace +`east`. -_**Console for West:**_ +_**West:**_ ~~~ shell -kubectl create namespace west -kubectl config set-context --current --namespace west +kubectl create deployment frontend --image quay.io/skupper/hello-world-frontend ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell -kubectl create namespace east -kubectl config set-context --current --namespace east +kubectl create deployment backend --image quay.io/skupper/hello-world-backend --replicas 3 ~~~ -## Step 5: Install Skupper in your namespaces - -The `skupper init` command installs the Skupper router and -controller in the current namespace. Run the `skupper init` command -in each namespace. +## Step 4: Create your sites -**Note:** If you are using Minikube, [you need to start `minikube -tunnel`][minikube-tunnel] before you install Skupper. +A Skupper _site_ is a location where components of your +application are running. Sites are linked together to form a +network for your application. In Kubernetes, a site is associated +with a namespace. -[minikube-tunnel]: https://skupper.io/start/minikube.html#running-minikube-tunnel +For each namespace, use `skupper init` to create a site. This +deploys the Skupper router and controller. Then use `skupper +status` to see the outcome. -_**Console for West:**_ +**Note:** If you are using Minikube, you need to [start minikube +tunnel][minikube-tunnel] before you run `skupper init`. -~~~ shell -skupper init --enable-console --enable-flow-collector -~~~ +[minikube-tunnel]: https://skupper.io/start/minikube.html#running-minikube-tunnel -_**Console for East:**_ +_**West:**_ ~~~ shell skupper init +skupper status ~~~ _Sample output:_ @@ -153,58 +158,54 @@ _Sample output:_ $ skupper init Waiting for LoadBalancer IP or hostname... Waiting for status... -Skupper is now installed in namespace ''. Use 'skupper status' to get more information. -~~~ - -## Step 6: Check the status of your namespaces - -Use `skupper status` in each console to check that Skupper is -installed. +Skupper is now installed in namespace 'west'. Use 'skupper status' to get more information. -_**Console for West:**_ - -~~~ shell -skupper status +$ skupper status +Skupper is enabled for namespace "west". It is not connected to any other sites. It has no exposed services. ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell +skupper init skupper status ~~~ _Sample output:_ ~~~ console +$ skupper init +Waiting for LoadBalancer IP or hostname... +Waiting for status... +Skupper is now installed in namespace 'east'. Use 'skupper status' to get more information. + $ skupper status -Skupper is enabled for namespace "" in interior mode. It is connected to 1 other site. It has 1 exposed service. -The site console url is: -The credentials for internal console-auth mode are held in secret: 'skupper-console-users' +Skupper is enabled for namespace "east". It is not connected to any other sites. It has no exposed services. ~~~ As you move through the steps below, you can use `skupper status` at any time to check your progress. -## Step 7: Link your namespaces +## Step 5: Link your sites Creating a link requires use of two `skupper` commands in conjunction, `skupper token create` and `skupper link create`. The `skupper token create` command generates a secret token that signifies permission to create a link. The token also carries the -link details. Then, in a remote namespace, The `skupper link -create` command uses the token to create a link to the namespace +link details. Then, in a remote site, The `skupper link +create` command uses the token to create a link to the site that generated it. **Note:** The link token is truly a *secret*. Anyone who has the -token can link to your namespace. Make sure that only those you -trust have access to it. +token can link to your site. Make sure that only those you trust +have access to it. -First, use `skupper token create` in one namespace to generate the -token. Then, use `skupper link create` in the other to create a -link. +First, use `skupper token create` in site West to generate the +token. Then, use `skupper link create` in site East to link +the sites. -_**Console for West:**_ +_**West:**_ ~~~ shell skupper token create ~/secret.token @@ -217,7 +218,7 @@ $ skupper token create ~/secret.token Token written to ~/secret.token ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell skupper link create ~/secret.token @@ -231,48 +232,31 @@ Site configured to link to https://10.105.193.154:8081/ed9c37f6-d78a-11ec-a8c7-0 Check the status of the link using 'skupper link status'. ~~~ -If your console sessions are on different machines, you may need +If your terminal sessions are on different machines, you may need to use `scp` or a similar tool to transfer the token securely. By default, tokens expire after a single use or 15 minutes after creation. -## Step 8: Fail on demand +## Step 6: Fail on demand -_**Console for West:**_ +_**West:**_ ~~~ shell if [ -n "${SKEWER_FAIL}" ]; then expr 1 / 0; fi ~~~ -## Step 9: Deploy the application +## Step 7: Expose the backend -Use `kubectl create deployment` to deploy the frontend and backend -services. +We now have our sites linked to form a Skupper network, but no +services are exposed on it. Skupper uses the `skupper expose` +command to select a service from one site for exposure in all the +linked sites. -_**Console for West:**_ - -~~~ shell -kubectl create deployment frontend --image quay.io/skupper/hello-world-frontend -~~~ +Use `skupper expose` to expose the backend service in East to +the frontend in West. -_**Console for East:**_ - -~~~ shell -kubectl create deployment backend --image quay.io/skupper/hello-world-backend --replicas 3 -~~~ - -## Step 10: Expose the backend service - -We now have two namespaces linked to form a Skupper network, but -no services are exposed on it. Skupper uses the `skupper -expose` command to select a service from one namespace for -exposure in all the linked namespaces. - -Use `skupper expose` to expose the backend service on the Skupper -network. - -_**Console for East:**_ +_**East:**_ ~~~ shell skupper expose deployment/backend --port 8080 @@ -285,11 +269,10 @@ $ skupper expose deployment/backend --port 8080 deployment backend exposed as backend ~~~ -## Step 11: Test the application +## Step 8: Access the frontend -We have established connectivity between the two namespaces and -made the backend available to the frontend. Before we can test -the application, we need external access to the frontend. +In order to use and test the application, we need external access +to the frontend. Use `kubectl expose` with `--type LoadBalancer` to open network access to the frontend service. @@ -304,7 +287,7 @@ request the `/api/health` endpoint at that address. **Note:** The `` field in the following commands is a placeholder. The actual value is an IP address. -_**Console for West:**_ +_**West:**_ ~~~ shell kubectl expose deployment/frontend --port 8080 --type LoadBalancer @@ -329,46 +312,12 @@ OK If everything is in order, you can now access the web interface by navigating to `http://:8080/` in your browser. -## Accessing the web console - -Skupper includes a web console you can use to view the application -network. To access it, use `skupper status` to look up the URL of -the web console. Then use `kubectl get -secret/skupper-console-users` to look up the console admin -password. - -**Note:** The `` and `` fields in the -following output are placeholders. The actual values are specific -to your environment. - -_**Console for West:**_ - -~~~ shell -skupper status -kubectl get secret/skupper-console-users -o jsonpath={.data.admin} | base64 -d -~~~ - -_Sample output:_ - -~~~ console -$ skupper status -Skupper is enabled for namespace "west". It is connected to 1 other site. It has 1 exposed service. -The site console url is: -The credentials for internal console-auth mode are held in secret: 'skupper-console-users' - -$ kubectl get secret/skupper-console-users -o jsonpath={.data.admin} | base64 -d - -~~~ - -Navigate to `` in your browser. When prompted, log -in as user `admin` and enter the password. - ## Cleaning up To remove Skupper and the other resources from this exercise, use -the following commands. +the following commands: -_**Console for West:**_ +_**West:**_ ~~~ shell skupper delete @@ -376,7 +325,7 @@ kubectl delete service/frontend kubectl delete deployment/frontend ~~~ -_**Console for East:**_ +_**East:**_ ~~~ shell skupper delete diff --git a/external/skewer-main/example/plano b/external/skewer/example/plano similarity index 100% rename from external/skewer-main/example/plano rename to external/skewer/example/plano diff --git a/external/skewer/example/python/plano b/external/skewer/example/python/plano new file mode 120000 index 0000000..2366248 --- /dev/null +++ b/external/skewer/example/python/plano @@ -0,0 +1 @@ +../../python/plano \ No newline at end of file diff --git a/external/skewer/example/python/skewer b/external/skewer/example/python/skewer new file mode 120000 index 0000000..d33ad4b --- /dev/null +++ b/external/skewer/example/python/skewer @@ -0,0 +1 @@ +../../python/skewer \ No newline at end of file diff --git a/external/skewer-main/example/skewer.yaml b/external/skewer/example/skewer.yaml similarity index 61% rename from external/skewer-main/example/skewer.yaml rename to external/skewer/example/skewer.yaml index bb36722..afc0b6b 100644 --- a/external/skewer-main/example/skewer.yaml +++ b/external/skewer/example/skewer.yaml @@ -19,21 +19,17 @@ sites: KUBECONFIG: ~/.kube/config-east steps: - standard: install_the_skupper_command_line_tool - - standard: configure_separate_console_sessions - - standard: access_your_clusters - - standard: set_up_your_namespaces - - standard: install_skupper_in_your_namespaces - - standard: check_the_status_of_your_namespaces - - standard: link_your_namespaces + - standard: kubernetes/set_up_your_namespaces + - standard: hello_world/deploy_the_frontend_and_backend + - standard: kubernetes/create_your_sites + - standard: link_your_sites - title: Fail on demand commands: west: - run: | if [ -n "${SKEWER_FAIL}" ]; then expr 1 / 0; fi - - standard: hello_world/deploy_the_application - - standard: hello_world/expose_the_backend_service - - standard: hello_world/test_the_application - - standard: accessing_the_web_console + - standard: hello_world/expose_the_backend + - standard: hello_world/access_the_frontend - standard: hello_world/cleaning_up summary: | A summary diff --git a/external/skewer-main/external/plano-main/.github/workflows/main.yaml b/external/skewer/external/plano/.github/workflows/main.yaml similarity index 100% rename from external/skewer-main/external/plano-main/.github/workflows/main.yaml rename to external/skewer/external/plano/.github/workflows/main.yaml diff --git a/external/skewer-main/external/plano-main/.gitignore b/external/skewer/external/plano/.gitignore similarity index 100% rename from external/skewer-main/external/plano-main/.gitignore rename to external/skewer/external/plano/.gitignore diff --git a/external/skewer-main/external/plano-main/LICENSE.txt b/external/skewer/external/plano/LICENSE.txt similarity index 100% rename from external/skewer-main/external/plano-main/LICENSE.txt rename to external/skewer/external/plano/LICENSE.txt diff --git a/external/skewer-main/external/plano-main/MANIFEST.in b/external/skewer/external/plano/MANIFEST.in similarity index 100% rename from external/skewer-main/external/plano-main/MANIFEST.in rename to external/skewer/external/plano/MANIFEST.in diff --git a/external/skewer-main/external/plano-main/Makefile b/external/skewer/external/plano/Makefile similarity index 100% rename from external/skewer-main/external/plano-main/Makefile rename to external/skewer/external/plano/Makefile diff --git a/external/skewer-main/external/plano-main/README.md b/external/skewer/external/plano/README.md similarity index 77% rename from external/skewer-main/external/plano-main/README.md rename to external/skewer/external/plano/README.md index 2bf8c99..fb4b1c2 100644 --- a/external/skewer-main/external/plano-main/README.md +++ b/external/skewer/external/plano/README.md @@ -12,7 +12,7 @@ To install plano globally for the current user: make install ~~~ -## Example 1 +## A self-contained command with subcommands `~/.local/bin/widget`: ~~~ python @@ -37,7 +37,7 @@ Hello OK (0s) ~~~ -## Example 2 +## A self-contained test command `~/.local/bin/widget-test`: ~~~ python @@ -46,7 +46,7 @@ from plano import * @test def check(): - run("widget --message Yo") + run("widget greeting --message Yo") if __name__ == "__main__": PlanoTestCommand(sys.modules[__name__]).main() @@ -71,6 +71,18 @@ Failed: 0 All tests passed ~~~ +## Programmatic test definition + +~~~ python +from plano import * + +def test_widget(message): + run(f"widget greeting --message {message}") + +for message in "hi", "lo", "in between": + add_test(f"message-{message}", test_widget, message) +~~~ + ## Things to know * The plano command accepts command sequences in the form "this,that" diff --git a/external/skewer-main/external/plano-main/bin/plano b/external/skewer/external/plano/bin/plano similarity index 100% rename from external/skewer-main/external/plano-main/bin/plano rename to external/skewer/external/plano/bin/plano diff --git a/external/skewer-main/external/plano-main/bin/plano-test b/external/skewer/external/plano/bin/plano-test similarity index 100% rename from external/skewer-main/external/plano-main/bin/plano-test rename to external/skewer/external/plano/bin/plano-test diff --git a/external/skewer-main/external/plano-main/docs/conf.py b/external/skewer/external/plano/docs/conf.py similarity index 100% rename from external/skewer-main/external/plano-main/docs/conf.py rename to external/skewer/external/plano/docs/conf.py diff --git a/external/skewer-main/external/plano-main/docs/index.rst b/external/skewer/external/plano/docs/index.rst similarity index 100% rename from external/skewer-main/external/plano-main/docs/index.rst rename to external/skewer/external/plano/docs/index.rst diff --git a/external/skewer-main/external/plano-main/pyproject.toml b/external/skewer/external/plano/pyproject.toml similarity index 100% rename from external/skewer-main/external/plano-main/pyproject.toml rename to external/skewer/external/plano/pyproject.toml diff --git a/external/skewer-main/external/plano-main/src/plano/__init__.py b/external/skewer/external/plano/src/plano/__init__.py similarity index 100% rename from external/skewer-main/external/plano-main/src/plano/__init__.py rename to external/skewer/external/plano/src/plano/__init__.py diff --git a/external/skewer-main/external/plano-main/src/plano/_testproject/.plano.py b/external/skewer/external/plano/src/plano/_testproject/.plano.py similarity index 97% rename from external/skewer-main/external/plano-main/src/plano/_testproject/.plano.py rename to external/skewer/external/plano/src/plano/_testproject/.plano.py index 67904b2..8cda2e7 100644 --- a/external/skewer-main/external/plano-main/src/plano/_testproject/.plano.py +++ b/external/skewer/external/plano/src/plano/_testproject/.plano.py @@ -35,7 +35,7 @@ def extended_command(alpha, beta, omega="y"): @command(parameters=[CommandParameter("message_", help="The message to print", display_name="message"), CommandParameter("count", help="Print the message COUNT times"), CommandParameter("extra", default=1, short_option="e")]) -def echo(message_, count=1, extra=None, trouble=False): +def echo(message_, count=1, extra=None, trouble=False, verbose=False): """ Print a message to the console """ diff --git a/external/skewer-main/external/plano-main/src/plano/_testproject/src/chucker/__init__.py b/external/skewer/external/plano/src/plano/_testproject/src/chucker/__init__.py similarity index 100% rename from external/skewer-main/external/plano-main/src/plano/_testproject/src/chucker/__init__.py rename to external/skewer/external/plano/src/plano/_testproject/src/chucker/__init__.py diff --git a/external/skewer/external/plano/src/plano/_testproject/src/chucker/moretests.py b/external/skewer/external/plano/src/plano/_testproject/src/chucker/moretests.py new file mode 100644 index 0000000..2607880 --- /dev/null +++ b/external/skewer/external/plano/src/plano/_testproject/src/chucker/moretests.py @@ -0,0 +1,24 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from plano import * + +@test +def hello_again(): + print("Hello again") diff --git a/external/skewer-main/external/plano-main/src/plano/_testproject/src/chucker/tests.py b/external/skewer/external/plano/src/plano/_testproject/src/chucker/tests.py similarity index 86% rename from external/skewer-main/external/plano-main/src/plano/_testproject/src/chucker/tests.py rename to external/skewer/external/plano/src/plano/_testproject/src/chucker/tests.py index a556cc8..4e0cec1 100644 --- a/external/skewer-main/external/plano-main/src/plano/_testproject/src/chucker/tests.py +++ b/external/skewer/external/plano/src/plano/_testproject/src/chucker/tests.py @@ -57,3 +57,14 @@ def process_error(): @test(disabled=True) def system_exit_(): exit(1) + +def test_widget(message): + print(message) + +for message in "hi", "lo", "in between": + add_test(f"message-{message}", test_widget, message) + +@test(disabled=True) +def badbye2(): + print("Badbye 2") + assert False diff --git a/external/skewer-main/external/plano-main/src/plano/_tests.py b/external/skewer/external/plano/src/plano/_tests.py similarity index 96% rename from external/skewer-main/external/plano-main/src/plano/_tests.py rename to external/skewer/external/plano/src/plano/_tests.py index c16ff98..cf67b89 100644 --- a/external/skewer-main/external/plano-main/src/plano/_tests.py +++ b/external/skewer/external/plano/src/plano/_tests.py @@ -25,6 +25,8 @@ import sys as _sys import threading as _threading +from .github import * + try: import http.server as _http except ImportError: # pragma: nocover @@ -52,15 +54,15 @@ def archive_operations(): assert is_file("some-dir.tar.gz"), list_dir() extract_archive("some-dir.tar.gz", output_dir="some-subdir") - assert is_dir("some-subdir/some-dir") - assert is_file("some-subdir/some-dir/some-file") + assert is_dir("some-subdir/some-dir"), list_dir("some-subdir") + assert is_file("some-subdir/some-dir/some-file"), list_dir("some-subdir/some-dir") rename_archive("some-dir.tar.gz", "something-else") - assert is_file("something-else.tar.gz") + assert is_file("something-else.tar.gz"), list_dir() extract_archive("something-else.tar.gz") - assert is_dir("something-else") - assert is_file("something-else/some-file") + assert is_dir("something-else"), list_dir() + assert is_file("something-else/some-file"), list_dir("something-else") @test def command_operations(): @@ -94,6 +96,7 @@ def run(self): print("Hello") SomeCommand().main([]) + SomeCommand().main(["--verbose"]) SomeCommand().main(["--interrupt"]) with expect_system_exit(): @@ -231,6 +234,8 @@ def env_operations(): with open(out, "w") as f: print_env(file=f) + print_stack() + @test def file_operations(): with working_dir(): @@ -309,10 +314,24 @@ def file_operations(): result = get_file_size(file) assert result == 10, result +@test +def github_operations(): + result = convert_github_markdown("# Hello, Fritz") + assert "Hello, Fritz" in result, result + + with working_dir(): + update_external_from_github("temp", "ssorj", "plano") + assert is_file("temp/Makefile"), list_dir("temp") + @test def http_operations(): class Handler(_http.BaseHTTPRequestHandler): def do_GET(self): + if not self.path.startswith("/api"): + self.send_response(404) + self.end_headers() + return + self.send_response(200) self.end_headers() self.wfile.write(b"[1]") @@ -341,7 +360,8 @@ def run(self): self.server.serve_forever() host, port = "localhost", get_random_port() - url = "http://{}:{}".format(host, port) + url = "http://{}:{}/api".format(host, port) + missing_url = "http://{}:{}/nono".format(host, port) try: server = _http.HTTPServer((host, port), Handler) @@ -358,6 +378,9 @@ def run(self): result = http_get(url) assert result == "[1]", result + with expect_error(): + http_get(missing_url) + result = http_get(url, insecure=True) assert result == "[1]", result @@ -526,6 +549,9 @@ def logging_operations(): with expect_error(): fail("Error!") + with expect_error(): + fail("Error! {}", "Let me elaborate") + for level in ("debug", "notice", "warning", "error"): with expect_output(contains="Hello") as out: with logging_disabled(): @@ -930,6 +956,7 @@ def test_operations(): with working_module_path("src"): import chucker import chucker.tests + import chucker.moretests print_tests(chucker.tests) @@ -951,6 +978,9 @@ def test_operations(): with expect_error(): run_tests(chucker.tests, enable="*badbye*", fail_fast=True, verbose=verbose) + with expect_error(): + run_tests([chucker.tests, chucker.moretests], enable="*badbye2*", fail_fast=True, verbose=verbose) + with expect_exception(KeyboardInterrupt): run_tests(chucker.tests, enable="keyboard-interrupt", verbose=verbose) @@ -970,6 +1000,7 @@ def run_command(*args): PlanoTestCommand(chucker.tests).main(args) run_command("--verbose") + run_command("--quiet") run_command("--list") with expect_system_exit(): @@ -1185,7 +1216,6 @@ def run_command(*args): with test_project(): run_command() run_command("--help") - run_command("--quiet") with expect_system_exit(): run_command("no-such-command") @@ -1197,6 +1227,8 @@ def run_command(*args): run_command("--help", "no-such-command") run_command("extended-command", "a", "b", "--omega", "z") + run_command("extended-command", "a", "b", "--omega", "z", "--verbose") + run_command("extended-command", "a", "b", "--omega", "z", "--quiet") with expect_system_exit(): run_command("echo") @@ -1273,6 +1305,8 @@ def run_command(*args): result = read_json("invisible.json") assert result == "nothing" + + def main(): PlanoTestCommand(_sys.modules[__name__]).main() diff --git a/external/skewer-main/external/plano-main/src/plano/command.py b/external/skewer/external/plano/src/plano/command.py similarity index 95% rename from external/skewer-main/external/plano-main/src/plano/command.py rename to external/skewer/external/plano/src/plano/command.py index eb2f54f..219f964 100644 --- a/external/skewer-main/external/plano-main/src/plano/command.py +++ b/external/skewer/external/plano/src/plano/command.py @@ -33,7 +33,7 @@ def parse_args(self, args): # pragma: nocover def configure_logging(self, args): return "warning", None - def init(self, args): + def init(self, args): # pragma: nocover raise NotImplementedError() def run(self): # pragma: nocover @@ -56,7 +56,7 @@ def main(self, args=None): except KeyboardInterrupt: pass except PlanoError as e: - if PLANO_DEBUG: + if PLANO_DEBUG: # pragma: nocover error(e) else: error(str(e)) @@ -80,6 +80,8 @@ def __init__(self, module=None, description="Run commands defined as Python func self.bound_commands = dict() self.running_commands = list() self.passthrough_args = None + self.verbose = False + self.quiet = False assert self.module is None or _inspect.ismodule(self.module), self.module @@ -135,7 +137,7 @@ def parse_args(self, args): return args def configure_logging(self, args): - if args.command is not None: + if args.command is not None and not self.bound_commands[args.command].passthrough: if args.verbose: return "debug", None @@ -152,9 +154,6 @@ def init(self, args): self.command_kwargs = dict() if args.command is not None: - self.verbose = args.verbose - self.quiet = args.quiet - for command in self.preceding_commands: command() @@ -253,13 +252,15 @@ def _process_commands(self): subparser = subparsers.add_parser(command.name, help=help, add_help=add_help, description=description, formatter_class=_argparse.RawDescriptionHelpFormatter) - subparser.add_argument("--verbose", action="store_true", - help="Print detailed logging to the console") - subparser.add_argument("--quiet", action="store_true", - help="Print no logging to the console") + + if not command.passthrough: + subparser.add_argument("--verbose", action="store_true", + help="Print detailed logging to the console") + subparser.add_argument("--quiet", action="store_true", + help="Print no logging to the console") for param in command.parameters.values(): - if param.name in ("verbose", "quiet"): + if not command.passthrough and param.name in ("verbose", "quiet"): continue if param.positional: @@ -302,6 +303,7 @@ def _process_commands(self): "dist": "Generate distribution artifacts", "install": "Install the built artifacts on your system", "test": "Run the tests", + "coverage": "Run the tests and measure code coverage", } def command(_function=None, name=None, parameters=None, parent=None, passthrough=False, hidden=False): @@ -320,11 +322,13 @@ def __init__(self, function): self.name = nvl(self.name, default) self.parameters = self._process_parameters(parameters) + self.passthrough = passthrough else: assert parameters is None self.name = nvl(self.name, self.parent.name) self.parameters = self.parent.parameters + self.passthrough = self.parent.passthrough doc = _inspect.getdoc(self.function) @@ -339,7 +343,6 @@ def __init__(self, function): self.help = nvl(self.help, self.parent.help) self.description = nvl(self.description, self.parent.description) - self.passthrough = passthrough self.hidden = hidden debug("Defining {}", self) diff --git a/external/skewer/external/plano/src/plano/github.py b/external/skewer/external/plano/src/plano/github.py new file mode 100644 index 0000000..8bfd9f8 --- /dev/null +++ b/external/skewer/external/plano/src/plano/github.py @@ -0,0 +1,78 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +from .main import * + +_html_template = """ + + + + + + +
+ +@content@ + +
+ + +""".strip() + +def convert_github_markdown(markdown): + json = emit_json({"text": markdown}) + content = http_post("https://api.github.com/markdown", json, content_type="application/json") + + # Remove the "user-content-" prefix from internal anchors + content = replace(content, "id=\"user-content-", "id=\"") + + return _html_template.replace("@content@", content) + +def update_external_from_github(dir, owner, repo, ref="main"): + dir = get_absolute_path(dir) + make_parent_dir(dir) + + url = f"https://github.com/{owner}/{repo}/archive/{ref}.tar.gz" + + with temp_file() as temp: + http_get(url, output_file=temp) + + with working_dir(quiet=True): + extract_archive(temp) + + extracted_dir = list_dir()[0] + assert is_dir(extracted_dir) + + move(extracted_dir, dir, inside=False) diff --git a/external/skewer-main/external/plano-main/src/plano/main.py b/external/skewer/external/plano/src/plano/main.py similarity index 95% rename from external/skewer-main/external/plano-main/src/plano/main.py rename to external/skewer/external/plano/src/plano/main.py index c7db173..e0fb31c 100644 --- a/external/skewer-main/external/plano-main/src/plano/main.py +++ b/external/skewer/external/plano/src/plano/main.py @@ -74,22 +74,21 @@ class PlanoTimeout(PlanoException): ## Archive operations def make_archive(input_dir, output_file=None, quiet=False): - """ - group: archive_operations - """ - check_program("tar") archive_stem = get_base_name(input_dir) if output_file is None: - output_file = "{}.tar.gz".format(join(get_current_dir(), archive_stem)) + # tar on Windows needs this + base = join(get_current_dir(), archive_stem) + base = base.replace("\\", "/") + + output_file = f"{base}.tar.gz" _notice(quiet, "Making archive {} from directory {}", repr(output_file), repr(input_dir)) - with working_dir(get_parent_dir(input_dir)): - run("tar -czf temp.tar.gz {}".format(archive_stem)) - move("temp.tar.gz", output_file) + with working_dir(get_parent_dir(input_dir), quiet=True): + run(f"tar -czf {output_file} {archive_stem}", quiet=True) return output_file @@ -103,13 +102,11 @@ def extract_archive(input_file, output_dir=None, quiet=False): input_file = get_absolute_path(input_file) - with working_dir(output_dir): - copy(input_file, "temp.tar.gz") + # tar on Windows needs this + input_file = input_file.replace("\\", "/") - try: - run("tar -xf temp.tar.gz") - finally: - remove("temp.tar.gz") + with working_dir(output_dir, quiet=True): + run(f"tar -xf {input_file}", quiet=True) return output_dir @@ -119,17 +116,20 @@ def rename_archive(input_file, new_archive_stem, quiet=False): output_dir = get_absolute_path(get_parent_dir(input_file)) output_file = "{}.tar.gz".format(join(output_dir, new_archive_stem)) + # tar on Windows needs this + output_file = output_file.replace("\\", "/") + input_file = get_absolute_path(input_file) - with working_dir(): - extract_archive(input_file) + with working_dir(quiet=True): + extract_archive(input_file, quiet=True) input_name = list_dir()[0] - input_dir = move(input_name, new_archive_stem) + input_dir = move(input_name, new_archive_stem, quiet=True) - make_archive(input_dir, output_file=output_file) + make_archive(input_dir, output_file=output_file, quiet=True) - remove(input_file) + remove(input_file, quiet=True) return output_file @@ -717,9 +717,11 @@ def print_json(data, **kwargs): ## HTTP operations def _run_curl(method, url, content=None, content_file=None, content_type=None, output_file=None, insecure=False, - user=None, password=None): + user=None, password=None, quiet=False): check_program("curl") + _notice(quiet, f"Sending {method} request to '{url}'") + args = ["curl", "-sfL"] if method != "GET": @@ -760,33 +762,37 @@ def _run_curl(method, url, content=None, content_file=None, content_type=None, o if output_file is None: return proc.stdout_result -def http_get(url, output_file=None, insecure=False, user=None, password=None): - return _run_curl("GET", url, output_file=output_file, insecure=insecure, user=user, password=password) +def http_get(url, output_file=None, insecure=False, user=None, password=None, quiet=False): + return _run_curl("GET", url, output_file=output_file, insecure=insecure, user=user, password=password, quiet=quiet) -def http_get_json(url, insecure=False, user=None, password=None): - return parse_json(http_get(url, insecure=insecure, user=user, password=password)) +def http_get_json(url, insecure=False, user=None, password=None, quiet=False): + return parse_json(http_get(url, insecure=insecure, user=user, password=password, quiet=quiet)) -def http_put(url, content, content_type=None, insecure=False, user=None, password=None): - _run_curl("PUT", url, content=content, content_type=content_type, insecure=insecure, user=user, password=password) +def http_put(url, content, content_type=None, insecure=False, user=None, password=None, quiet=False): + _run_curl("PUT", url, content=content, content_type=content_type, insecure=insecure, user=user, password=password, + quiet=quiet) -def http_put_file(url, content_file, content_type=None, insecure=False, user=None, password=None): +def http_put_file(url, content_file, content_type=None, insecure=False, user=None, password=None, quiet=False): _run_curl("PUT", url, content_file=content_file, content_type=content_type, insecure=insecure, user=user, - password=password) + password=password, quiet=quiet) -def http_put_json(url, data, insecure=False, user=None, password=None): - http_put(url, emit_json(data), content_type="application/json", insecure=insecure, user=user, password=password) +def http_put_json(url, data, insecure=False, user=None, password=None, quiet=False): + http_put(url, emit_json(data), content_type="application/json", insecure=insecure, user=user, password=password, + quiet=quiet) -def http_post(url, content, content_type=None, output_file=None, insecure=False, user=None, password=None): +def http_post(url, content, content_type=None, output_file=None, insecure=False, user=None, password=None, + quiet=False): return _run_curl("POST", url, content=content, content_type=content_type, output_file=output_file, - insecure=insecure, user=user, password=password) + insecure=insecure, user=user, password=password, quiet=quiet) -def http_post_file(url, content_file, content_type=None, output_file=None, insecure=False, user=None, password=None): +def http_post_file(url, content_file, content_type=None, output_file=None, insecure=False, user=None, password=None, + quiet=False): return _run_curl("POST", url, content_file=content_file, content_type=content_type, output_file=output_file, - insecure=insecure, user=user, password=password) + insecure=insecure, user=user, password=password, quiet=quiet) -def http_post_json(url, data, insecure=False, user=None, password=None): +def http_post_json(url, data, insecure=False, user=None, password=None, quiet=False): return parse_json(http_post(url, emit_json(data), content_type="application/json", insecure=insecure, user=user, - password=password)) + password=password, quiet=quiet)) ## Link operations @@ -1478,11 +1484,11 @@ def make_temp_dir(prefix="plano-", suffix="", dir=None): return _tempfile.mkdtemp(prefix=prefix, suffix=suffix, dir=dir) class temp_file: - def __init__(self, suffix="", dir=None): + def __init__(self, prefix="plano-", suffix="", dir=None): if dir is None: dir = get_system_temp_dir() - self.fd, self.file = _tempfile.mkstemp(prefix="plano-", suffix=suffix, dir=dir) + self.fd, self.file = _tempfile.mkstemp(prefix=prefix, suffix=suffix, dir=dir) def __enter__(self): return self.file @@ -1494,8 +1500,8 @@ def __exit__(self, exc_type, exc_value, traceback): remove(self.file, quiet=True) class temp_dir: - def __init__(self, suffix="", dir=None): - self.dir = make_temp_dir(suffix=suffix, dir=dir) + def __init__(self, prefix="plano-", suffix="", dir=None): + self.dir = make_temp_dir(prefix=prefix, suffix=suffix, dir=dir) def __enter__(self): return self.dir diff --git a/external/skewer-main/external/plano-main/src/plano/test.py b/external/skewer/external/plano/src/plano/test.py similarity index 96% rename from external/skewer-main/external/plano-main/src/plano/test.py rename to external/skewer/external/plano/src/plano/test.py index 0c2c6ae..fb87d8d 100644 --- a/external/skewer-main/external/plano-main/src/plano/test.py +++ b/external/skewer/external/plano/src/plano/test.py @@ -23,8 +23,10 @@ import argparse as _argparse import asyncio as _asyncio import fnmatch as _fnmatch +import functools as _functools import importlib as _importlib import inspect as _inspect +import sys as _sys import traceback as _traceback class PlanoTestCommand(BaseCommand): @@ -103,15 +105,20 @@ def run(self): class PlanoTestSkipped(Exception): pass -def test(_function=None, name=None, timeout=None, disabled=False): +def test(_function=None, name=None, module=None, timeout=None, disabled=False): class Test: def __init__(self, function): self.function = function - self.name = nvl(name, self.function.__name__.rstrip("_").replace("_", "-")) + self.name = name + self.module = module self.timeout = timeout self.disabled = disabled - self.module = _inspect.getmodule(self.function) + if self.name is None: + self.name = self.function.__name__.strip("_").replace("_", "-") + + if self.module is None: + self.module = _inspect.getmodule(self.function) if not hasattr(self.module, "_plano_tests"): self.module._plano_tests = list() @@ -136,6 +143,9 @@ def __repr__(self): else: return Test(_function) +def add_test(name, func, *args, **kwargs): + test(_functools.partial(func, *args, **kwargs), name=name, module=_inspect.getmodule(func)) + def skip_test(reason=None): if _inspect.stack()[2].frame.f_locals["unskipped"]: return diff --git a/external/skewer-main/plano b/external/skewer/plano similarity index 100% rename from external/skewer-main/plano rename to external/skewer/plano diff --git a/external/skewer/python/plano b/external/skewer/python/plano new file mode 120000 index 0000000..e9b6dc5 --- /dev/null +++ b/external/skewer/python/plano @@ -0,0 +1 @@ +../external/plano/src/plano \ No newline at end of file diff --git a/external/skewer-main/python/skewer/__init__.py b/external/skewer/python/skewer/__init__.py similarity index 100% rename from external/skewer-main/python/skewer/__init__.py rename to external/skewer/python/skewer/__init__.py diff --git a/external/skewer-main/python/skewer/main.py b/external/skewer/python/skewer/main.py similarity index 96% rename from external/skewer-main/python/skewer/main.py rename to external/skewer/python/skewer/main.py index c16be4e..b9aed11 100644 --- a/external/skewer-main/python/skewer/main.py +++ b/external/skewer/python/skewer/main.py @@ -96,7 +96,7 @@ def await_http_ok(service, url_template, user=None, password=None, timeout=240): notice(f"Waiting for HTTP OK from {url}") try: - http_get(url, insecure=insecure, user=user, password=password) + http_get(url, insecure=insecure, user=user, password=password, quiet=True) except PlanoError: if get_time() - start_time > timeout: fail(f"Timed out waiting for HTTP OK from {url}") @@ -179,7 +179,7 @@ def run_step(model, step, work_dir, check=True): def pause_for_demo(model): notice("Pausing for demo time") - first_site = list([x for _, x in model.sites])[0] + first_site = [x for _, x in model.sites][0] console_url = None password = None frontend_url = None @@ -187,7 +187,7 @@ def pause_for_demo(model): if first_site.platform == "kubernetes": with first_site: if resource_exists("service/frontend"): - if rsource_jsonpath("service/frontend", ".spec.type") == "LoadBalancer": + if get_resource_jsonpath("service/frontend", ".spec.type") == "LoadBalancer": frontend_ip = await_external_ip("service/frontend") frontend_url = f"http://{frontend_ip}:8080/" @@ -357,7 +357,7 @@ def generate_readme_step(model, step): site = dict(model.sites)[site_name] outputs = list() - out.append(f"_**Console for {site.title}:**_") + out.append(f"_**{site.title}:**_") out.append("") out.append("~~~ shell") @@ -416,7 +416,16 @@ def apply_standard_steps(model): def apply_attribute(name, default=None): if name not in step.data: - step.data[name] = standard_step_data.get(name, default) + value = standard_step_data.get(name, default) + + if value and name in ("title", "preamble", "postamble"): + for i, site in enumerate([x for _, x in model.sites]): + value = value.replace(f"@site{i}@", site.title) + + if site.namespace: + value = value.replace(f"@namespace{i}@", site.namespace) + + step.data[name] = value apply_attribute("name") apply_attribute("title") @@ -424,12 +433,17 @@ def apply_attribute(name, default=None): apply_attribute("preamble") apply_attribute("postamble") + platform = standard_step_data.get("platform") + if "commands" not in step.data and "commands" in standard_step_data: step.data["commands"] = dict() for i, item in enumerate(dict(model.sites).items()): site_name, site = item + if platform and site.platform != platform: + continue + if str(i) in standard_step_data["commands"]: # Is a specific index in the standard commands? commands = standard_step_data["commands"][str(i)] @@ -523,7 +537,7 @@ def __repr__(self): return f"model '{self.skewer_file}'" def check(self): - check_required_attributes(self, "title", "subtitle", "sites", "steps") + check_required_attributes(self, "title", "sites", "steps") check_unknown_attributes(self) for _, site in self.sites: diff --git a/external/skewer-main/python/skewer/planocommands.py b/external/skewer/python/skewer/planocommands.py similarity index 64% rename from external/skewer-main/python/skewer/planocommands.py rename to external/skewer/python/skewer/planocommands.py index cb696bf..754fb1e 100644 --- a/external/skewer-main/python/skewer/planocommands.py +++ b/external/skewer/python/skewer/planocommands.py @@ -18,40 +18,9 @@ # from plano import * +from plano.github import * from skewer import * -_render_template = """ - - - - - - -
- -@content@ - -
- - -""".strip() - _debug_param = CommandParameter("debug", help="Produce extra debug output on failure") @command @@ -62,17 +31,14 @@ def generate(output="README.md"): generate_readme("skewer.yaml", output) @command -def render(verbose=False, quiet=False): +def render(quiet=False): """ Render README.html from README.md """ generate() markdown = read("README.md") - data = {"text": markdown} - json = emit_json(data) - content = http_post("https://api.github.com/markdown", json, content_type="application/json") - html = _render_template.replace("@content@", content) + html = convert_github_markdown(markdown) write("README.html", html) @@ -121,9 +87,5 @@ def update_skewer(): This results in local changes to review and commit. """ - check_program("curl") - - make_dir("external") - remove("external/skewer-main") - run("curl -sfL https://github.com/skupperproject/skewer/archive/main.tar.gz | tar -C external -xz", shell=True) - copy("external/skewer-main/config/.github/workflows/main.yaml", ".github/workflows/main.yaml") + update_external_from_github("external/skewer", "skupperproject", "skewer") + copy("external/skewer/config/.github/workflows/main.yaml", ".github/workflows/main.yaml") diff --git a/external/skewer/python/skewer/standardsteps.yaml b/external/skewer/python/skewer/standardsteps.yaml new file mode 100644 index 0000000..ca128c0 --- /dev/null +++ b/external/skewer/python/skewer/standardsteps.yaml @@ -0,0 +1,292 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +install_the_skupper_command_line_tool: + title: Install the Skupper command-line tool + preamble: | + This example uses the Skupper command-line tool to deploy Skupper. + You need to install the `skupper` command only once for each + development environment. + + On Linux or Mac, you can use the install script (inspect it + [here][install-script]) to download and extract the command: + + ~~~ shell + curl https://skupper.io/install.sh | sh + ~~~ + + The script installs the command under your home directory. It + prompts you to add the command to your path if necessary. + + For Windows and other installation options, see [Installing + Skupper][install-docs]. + + [install-script]: https://github.com/skupperproject/skupper-website/blob/main/input/install.sh + [install-docs]: https://skupper.io/install/ +kubernetes/set_up_your_namespaces: + title: Set up your namespaces + platform: kubernetes + preamble: | + Skupper is designed for use with multiple Kubernetes namespaces, + usually on different clusters. The `skupper` and `kubectl` + commands use your [kubeconfig][kubeconfig] and current context to + select the namespace where they operate. + + [kubeconfig]: https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/ + + Your kubeconfig is stored in a file in your home directory. The + `skupper` and `kubectl` commands use the `KUBECONFIG` environment + variable to locate it. + + A single kubeconfig supports only one active context per user. + Since you will be using multiple contexts at once in this + exercise, you need to create distinct kubeconfigs. + + For each namespace, open a new terminal window. In each terminal, + set the `KUBECONFIG` environment variable to a different path and + log in to your cluster. Then create the namespace you wish to use + and set the namespace on your current context. + + **Note:** The login procedure varies by provider. See the + documentation for yours: + + * [Minikube](https://skupper.io/start/minikube.html#cluster-access) + * [Amazon Elastic Kubernetes Service (EKS)](https://skupper.io/start/eks.html#cluster-access) + * [Azure Kubernetes Service (AKS)](https://skupper.io/start/aks.html#cluster-access) + * [Google Kubernetes Engine (GKE)](https://skupper.io/start/gke.html#cluster-access) + * [IBM Kubernetes Service](https://skupper.io/start/ibmks.html#cluster-access) + * [OpenShift](https://skupper.io/start/openshift.html#cluster-access) + commands: + "*": + - run: export KUBECONFIG=@kubeconfig@ + - run: "# Enter your provider-specific login command" + - run: kubectl create namespace @namespace@ + - run: kubectl config set-context --current --namespace @namespace@ +kubernetes/set_up_your_kubernetes_namespace: + title: Set up your Kubernetes namespace + platform: kubernetes + preamble: | + Open a new terminal window and log in to your cluster. Then + create the namespace you wish to use and set the namespace on your + current context. + + **Note:** The login procedure varies by provider. See the + documentation for your chosen providers: + + * [Minikube](https://skupper.io/start/minikube.html#cluster-access) + * [Amazon Elastic Kubernetes Service (EKS)](https://skupper.io/start/eks.html#cluster-access) + * [Azure Kubernetes Service (AKS)](https://skupper.io/start/aks.html#cluster-access) + * [Google Kubernetes Engine (GKE)](https://skupper.io/start/gke.html#cluster-access) + * [IBM Kubernetes Service](https://skupper.io/start/ibmks.html#cluster-access) + * [OpenShift](https://skupper.io/start/openshift.html#cluster-access) + commands: + "*": + - run: "# Enter your provider-specific login command" + - run: kubectl create namespace @namespace@ + - run: kubectl config set-context --current --namespace @namespace@ +kubernetes/create_your_sites: + title: Create your sites + platform: kubernetes + preamble: | + A Skupper _site_ is a location where components of your + application are running. Sites are linked together to form a + network for your application. In Kubernetes, a site is associated + with a namespace. + + For each namespace, use `skupper init` to create a site. This + deploys the Skupper router and controller. Then use `skupper + status` to see the outcome. + + **Note:** If you are using Minikube, you need to [start minikube + tunnel][minikube-tunnel] before you run `skupper init`. + + [minikube-tunnel]: https://skupper.io/start/minikube.html#running-minikube-tunnel + commands: + "0": + - run: skupper init + output: | + Waiting for LoadBalancer IP or hostname... + Waiting for status... + Skupper is now installed in namespace 'west'. Use 'skupper status' to get more information. + - run: skupper status + output: | + Skupper is enabled for namespace "west". It is not connected to any other sites. It has no exposed services. + "*": + - run: skupper init + output: | + Waiting for LoadBalancer IP or hostname... + Waiting for status... + Skupper is now installed in namespace 'east'. Use 'skupper status' to get more information. + - run: skupper status + output: | + Skupper is enabled for namespace "east". It is not connected to any other sites. It has no exposed services. + postamble: | + As you move through the steps below, you can use `skupper status` at + any time to check your progress. +podman/set_up_your_podman_network: + title: Set up your Podman network + platform: podman + preamble: | + Open a new terminal window and set the `SKUPPER_PLATFORM` + environment variable to `podman`. This sets the Skupper platform + to Podman for this terminal session. + + Use `podman network create` to create the Podman network that + Skupper will use. + + Use `systemctl` to enable the Podman API service. + commands: + "*": + - run: export SKUPPER_PLATFORM=podman + - run: podman network create skupper + apply: readme + - run: if ! podman network exists skupper; then podman network create skupper; fi + apply: test + - run: systemctl --user enable --now podman.socket + postamble: | + If the `systemctl` command doesn't work, you can try the `podman + system service` command instead: + + ~~~ + podman system service --time=0 unix://$XDG_RUNTIME_DIR/podman/podman.sock & + ~~~ +link_your_sites: + title: Link your sites + preamble: | + Creating a link requires use of two `skupper` commands in + conjunction, `skupper token create` and `skupper link create`. + + The `skupper token create` command generates a secret token that + signifies permission to create a link. The token also carries the + link details. Then, in a remote site, The `skupper link + create` command uses the token to create a link to the site + that generated it. + + **Note:** The link token is truly a *secret*. Anyone who has the + token can link to your site. Make sure that only those you trust + have access to it. + + First, use `skupper token create` in site @site0@ to generate the + token. Then, use `skupper link create` in site @site1@ to link + the sites. + commands: + "0": + - run: skupper token create ~/secret.token + output: Token written to ~/secret.token + "1": + - run: skupper link create ~/secret.token + output: | + Site configured to link to https://10.105.193.154:8081/ed9c37f6-d78a-11ec-a8c7-04421a4c5042 (name=link1) + Check the status of the link using 'skupper link status'. + - run: skupper link status --wait 60 + apply: test + postamble: | + If your terminal sessions are on different machines, you may need + to use `scp` or a similar tool to transfer the token securely. By + default, tokens expire after a single use or 15 minutes after + creation. +cleaning_up: + name: cleaning_up + title: Cleaning up + numbered: false + preamble: | + To remove Skupper and the other resources from this exercise, use + the following commands. + commands: + "*": + - run: skupper delete +hello_world/deploy_the_frontend_and_backend: + title: Deploy the frontent and backend + preamble: | + This example runs the frontend and the backend in separate + Kubernetes namespaces, on different clusters. + + Use `kubectl create deployment` to deploy the frontend in + namespace `@namespace0@` and the backend in namespace + `@namespace1@`. + commands: + "0": + - run: kubectl create deployment frontend --image quay.io/skupper/hello-world-frontend + "1": + - run: kubectl create deployment backend --image quay.io/skupper/hello-world-backend --replicas 3 +hello_world/expose_the_backend: + title: Expose the backend + preamble: | + We now have our sites linked to form a Skupper network, but no + services are exposed on it. Skupper uses the `skupper expose` + command to select a service from one site for exposure in all the + linked sites. + + Use `skupper expose` to expose the backend service in @site1@ to + the frontend in @site0@. + commands: + "1": + - await_resource: deployment/backend + - run: skupper expose deployment/backend --port 8080 + output: deployment backend exposed as backend +hello_world/access_the_frontend: + title: Access the frontend + preamble: | + In order to use and test the application, we need external access + to the frontend. + + Use `kubectl expose` with `--type LoadBalancer` to open network + access to the frontend service. + + Once the frontend is exposed, use `kubectl get service/frontend` + to look up the external IP of the frontend service. If the + external IP is ``, try again after a moment. + + Once you have the external IP, use `curl` or a similar tool to + request the `/api/health` endpoint at that address. + + **Note:** The `` field in the following commands is a + placeholder. The actual value is an IP address. + commands: + "0": + - run: kubectl expose deployment/frontend --port 8080 --type LoadBalancer + output: service/frontend exposed + - await_resource: service/frontend + - run: kubectl get service/frontend + apply: readme + output: | + NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE + frontend LoadBalancer 10.103.232.28 8080:30407/TCP 15s + - run: curl http://:8080/api/health + apply: readme + output: OK + - await_http_ok: [service/frontend, "http://{}:8080/api/health"] + postamble: | + If everything is in order, you can now access the web interface by + navigating to `http://:8080/` in your browser. +hello_world/cleaning_up: + name: cleaning_up + title: Cleaning up + numbered: false + preamble: | + To remove Skupper and the other resources from this exercise, use + the following commands: + commands: + "0": + - run: skupper delete + - run: kubectl delete service/frontend + - run: kubectl delete deployment/frontend + "1": + - run: skupper delete + - run: kubectl delete deployment/backend diff --git a/external/skewer-main/python/skewer/standardtext.yaml b/external/skewer/python/skewer/standardtext.yaml similarity index 100% rename from external/skewer-main/python/skewer/standardtext.yaml rename to external/skewer/python/skewer/standardtext.yaml diff --git a/external/skewer-main/python/skewer/tests.py b/external/skewer/python/skewer/tests.py similarity index 100% rename from external/skewer-main/python/skewer/tests.py rename to external/skewer/python/skewer/tests.py diff --git a/python/plano b/python/plano index a578137..431570b 120000 --- a/python/plano +++ b/python/plano @@ -1 +1 @@ -../external/skewer-main/python/plano \ No newline at end of file +../external/skewer/python/plano \ No newline at end of file diff --git a/python/skewer b/python/skewer index 6ea41b9..0cc66e2 120000 --- a/python/skewer +++ b/python/skewer @@ -1 +1 @@ -../external/skewer-main/python/skewer \ No newline at end of file +../external/skewer/python/skewer \ No newline at end of file diff --git a/skewer.yaml b/skewer.yaml index ce0cf93..ec12c7c 100644 --- a/skewer.yaml +++ b/skewer.yaml @@ -39,8 +39,6 @@ sites: env: KUBECONFIG: ~/.kube/config-east steps: - - standard: configure_separate_console_sessions - - standard: access_your_clusters - title: Install Skupper in your clusters preamble: | Use the `kubectl apply` command to install the Skupper @@ -62,7 +60,7 @@ steps: clusterrole.rbac.authorization.k8s.io/skupper-site-controller created clusterrolebinding.rbac.authorization.k8s.io/skupper-site-controller created deployment.apps/skupper-site-controller created - - standard: set_up_your_namespaces + - standard: kubernetes/set_up_your_namespaces - title: Apply your YAML resources preamble: | To configure our example sites and service bindings, we are @@ -99,14 +97,12 @@ steps: name: skupper-site data: name: west - console: "true" - flow-collector: "true" ~~~ #### Resources in East Like the one for West, here is the Skupper site definition for - the East. It includes the `ingress: "false"` setting since no + the East. It includes the `ingress: none` setting since no ingress is required at this site for the Hello World example. [site.yaml](east/site.yaml): @@ -118,7 +114,7 @@ steps: name: skupper-site data: name: east - ingress: "false" + ingress: none ~~~ In East, the `backend` deployment has an annotation named @@ -169,7 +165,6 @@ steps: deployment.apps/frontend created - await_resource: deployment/skupper-service-controller - await_resource: deployment/frontend - - await_external_ip: service/skupper east: - run: kubectl apply -f east/site.yaml -f east/backend.yaml output: | @@ -177,7 +172,7 @@ steps: deployment.apps/backend created - await_resource: deployment/skupper-service-controller - await_resource: deployment/backend - - standard: link_your_namespaces + - standard: link_your_sites preamble: | You can configure sites and service bindings declaratively, but linking sites is different. To create a link, you must have the @@ -207,8 +202,7 @@ steps: East to create a link. [install]: https://skupper.io/install/index.html - - standard: hello_world/test_the_application - - standard: accessing_the_web_console + - standard: hello_world/access_the_frontend - standard: cleaning_up commands: west: diff --git a/west/site.yaml b/west/site.yaml index 3a07b09..87ed2bf 100644 --- a/west/site.yaml +++ b/west/site.yaml @@ -4,5 +4,3 @@ metadata: name: skupper-site data: name: west - console: "true" - flow-collector: "true"