├── automatedtest ├── finish.md ├── intro.md ├── step2.md ├── foreground.sh ├── step1.md ├── index.json └── .cypress │ └── example_spec.js ├── embed-videos ├── finish.md ├── index.json ├── step1.md └── intro.md ├── placeholders ├── finish.md ├── intro.md ├── step3.md ├── foreground1.sh ├── foreground2.sh ├── step1.md ├── step2.md └── index.json ├── displaying-web-ui ├── finish.md ├── intro.md ├── step1.md └── index.json ├── quiz ├── finish.md ├── intro.md ├── index.json └── step1.md ├── credit-where-credit-due ├── intro.md ├── step1.md ├── finish.md └── index.json ├── custom-events ├── site │ ├── myapp.scss │ ├── index.html │ ├── site.tar.gz │ ├── build-zip.sh │ ├── server │ │ └── template.html │ ├── package.json │ └── gulpfile.js ├── intro.md ├── env-init.sh ├── index.json └── step1.md ├── environment-usages ├── bash │ ├── finish.md │ ├── step1.md │ └── index.json ├── go │ ├── finish.md │ ├── step1.md │ └── index.json ├── java │ ├── finish.md │ ├── step1.md │ └── index.json ├── r │ ├── finish.md │ ├── step1.md │ └── index.json ├── ruby │ ├── finish.md │ ├── step1.md │ └── index.json ├── rust │ ├── finish.md │ ├── step1.md │ └── index.json ├── dotnet │ ├── finish.md │ ├── step1.md │ └── index.json ├── kotlin │ ├── finish.md │ ├── step1.md │ └── index.json ├── nodejs │ ├── finish.md │ ├── step1.md │ └── index.json ├── python │ ├── finish.md │ ├── step1.md │ └── index.json ├── python-3-7 │ ├── finish.md │ ├── foreground.sh │ ├── step1.md │ └── index.json ├── python-3-8 │ ├── finish.md │ ├── foreground.sh │ ├── step1.md │ └── index.json ├── README.md ├── kubernetes-cluster-running │ ├── step1.md │ ├── finish.md │ └── index.json ├── minikube │ ├── step1.md │ ├── finish.md │ └── index.json ├── kubernetes-cluster │ ├── step2.md │ ├── finish.md │ ├── step1.md │ └── index.json ├── ubuntu-2004 │ ├── finish.md │ ├── step1.md │ └── index.json └── ubuntu-1804 │ ├── finish.md │ ├── step1.md │ └── index.json ├── instruction-guided-template ├── finish.md ├── .cypress │ └── instruction_guided_spec.js ├── step2.md ├── intro.md ├── foreground.sh ├── step1.md └── index.json ├── opening-multiple-terminals ├── finish.md ├── intro.md ├── step1.md └── index.json ├── use-ip-or-dns-for-environment ├── intro.md ├── finish.md ├── foreground.sh ├── step1.md └── index.json ├── environments ├── kubernetes-cluster-1-16 │ ├── node │ │ ├── build │ │ │ └── 1_init.sh │ │ └── katacoda.yaml │ ├── katacoda.yaml │ └── master │ │ ├── katacoda.yaml │ │ └── build │ │ └── 1_init.sh ├── exampleenv1 │ ├── katacoda.yaml │ └── build │ │ ├── 2_pulldockerimages.sh │ │ ├── 3_pulldemos.sh │ │ ├── 1_installpackages.sh │ │ └── 4_configure_environment.sh ├── kc-custom-env-vscode-extensions │ ├── katacoda.yaml │ └── build │ │ └── 1_init.sh ├── multinodeexampleenv-cluster │ ├── node │ │ ├── katacoda.yaml │ │ └── build │ │ │ ├── 2_pulldockerimages.sh │ │ │ ├── 3_pulldemos.sh │ │ │ └── 1_installpackages.sh │ └── master │ │ ├── katacoda.yaml │ │ └── build │ │ ├── 2_pulldockerimages.sh │ │ ├── 3_pulldemos.sh │ │ └── 1_installpackages.sh └── README.md ├── notebooks-py ├── requirements.txt ├── index.json └── Index.ipynb ├── delivering-training └── creating-training-pathway-json │ ├── finish.md │ ├── intro.md │ ├── index.json │ └── step1.md ├── challenges ├── challenge-linux-solver │ ├── install-solver.sh │ ├── assets │ │ ├── my-xargs.sh │ │ ├── streetcar.pdf │ │ ├── solutions.sh.enc │ │ ├── cow-311266_640.png │ │ ├── learning-modern-linux-book.jpg │ │ ├── solutions.sh.md │ │ ├── solutions.sh │ │ ├── verifications.sh │ │ └── hints.md │ ├── task_1.md │ ├── verify.sh │ ├── hint.sh │ ├── task_2.md │ ├── task_4.md │ ├── finish.md │ ├── readme.md │ ├── task_3.md │ ├── intro.md │ ├── index.json │ ├── .cypress │ │ └── solver_spec.js.disabled │ ├── init-background.sh │ └── init-foreground.sh ├── kubernetes-challenge-1 │ ├── foreground.sh │ ├── 1_verify.sh │ ├── 2_verify.sh │ ├── 2_task.md │ ├── intro.md │ ├── assets │ │ ├── cheat │ │ ├── cheat2 │ │ ├── tux.png │ │ └── .DS_Store │ ├── .DS_Store │ ├── finish.md │ ├── configure.sh │ ├── 1_task.md │ ├── 1_hint.sh │ ├── .cypress │ │ └── k8s_spec.js │ └── index.json ├── node-challenge-1 │ ├── 1_task.md │ ├── intro.md │ ├── 1_verify.sh │ ├── finish.md │ ├── assets │ │ └── cheat │ ├── background.sh │ ├── .cypress │ │ └── node_spec.js │ └── index.json └── challenge-hello-world │ ├── 1_task.md │ ├── 1_verify.sh │ ├── 2_verify.sh │ ├── 2_task.md │ ├── finish.md │ ├── intro.md │ ├── 2_hint.sh │ ├── SOLUTIONS.md │ ├── assets │ └── example-asset.txt │ ├── 1_hint.sh │ ├── index.json │ └── README.md ├── uilayouts ├── uilayout-editor-terminal │ ├── setup.sh │ ├── intro.md │ ├── index.json │ └── step1.md ├── uilayout-vscode-terminal │ ├── intro.md │ ├── index.json │ └── step1.md ├── uilayout-iframe │ ├── step1.md │ └── index.json ├── uilayout-terminal │ ├── step1.md │ └── index.json ├── uilayout-terminal-terminal │ ├── step1.md │ └── index.json ├── uilayout-terminal-iframe-split │ ├── index.json │ └── step1.md ├── uilayout-terminal-iframe │ ├── index.json │ └── step1.md └── uilayout-terminal-with-vscode │ ├── index.json │ └── step1.md ├── displaying-progress-spinner ├── foreground.sh ├── background.sh ├── intro.md ├── step1.md ├── index.json └── assets │ └── wait.sh ├── assets ├── avatar.png └── test.css ├── upload-assets ├── assets │ ├── deploy.sh │ └── wait.sh ├── foreground.sh ├── background.sh ├── index.json └── step1.md ├── run-commands-automatically ├── background.sh ├── step2-background.sh ├── step2-foreground.sh ├── foreground.sh ├── intro.md ├── step3-foreground.sh ├── step3-background.sh ├── step2.md ├── step1.md ├── step3.md └── index.json ├── verified-steps ├── step1-verify.sh ├── step2-answer.md ├── step2.md ├── index.json └── step1.md ├── README.md ├── course-data-host01-reference ├── intro.md ├── init-env.sh ├── set-env.sh ├── step1.md ├── assets │ ├── nginx.yaml │ └── wait-script.sh └── index.json ├── create-course ├── finish.md ├── intro.md ├── step2.md ├── index.json ├── step4.md ├── step1.md └── step3.md ├── notebooks-py-slim ├── index.json └── Index.ipynb ├── .dev ├── docker-compose.yml ├── Dockerfile ├── README.md └── Makefile ├── displaying-images ├── assets │ └── logo-text-with-head.png ├── step1.md └── index.json ├── create-scenario-101 ├── intro.md ├── finish.md ├── step4.md ├── step5.md ├── step2.md ├── index.json ├── step1.md └── step3.md ├── strings.yaml ├── vscode-extensions ├── foreground.sh ├── setup.sh ├── index.json └── step1.md ├── delivering-training-pathway.json ├── writing-files ├── index.json └── step1.md ├── clipboard ├── index.json └── step1.md ├── markdown-extensions ├── index.json └── step1.md ├── visualise-docker ├── index.json └── step1.md ├── exclusion-patterns ├── index.json └── step1.md ├── training └── training-example-pathway.json ├── dashboard-tabs-iframe ├── index.json └── step1.md ├── challenges-pathway.json ├── dashboard-tabs ├── index.json ├── step1.md └── step2.md ├── uilayouts-pathway.json ├── environment-usages-pathway.json └── profile-pathway.json /automatedtest/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /embed-videos/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /placeholders/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /placeholders/intro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /placeholders/step3.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /displaying-web-ui/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /displaying-web-ui/intro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quiz/finish.md: -------------------------------------------------------------------------------- 1 | Great work! -------------------------------------------------------------------------------- /credit-where-credit-due/intro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /credit-where-credit-due/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /custom-events/site/myapp.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/bash/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/bash/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/go/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/go/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/java/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/java/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/r/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/r/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/ruby/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/ruby/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/rust/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/rust/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/dotnet/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/dotnet/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/kotlin/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/kotlin/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/nodejs/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/nodejs/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/python/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/python/step1.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /instruction-guided-template/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /opening-multiple-terminals/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /opening-multiple-terminals/intro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /use-ip-or-dns-for-environment/intro.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/python-3-7/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environment-usages/python-3-8/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /use-ip-or-dns-for-environment/finish.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /environments/kubernetes-cluster-1-16/node/build/1_init.sh: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /notebooks-py/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | voluptuous 3 | -------------------------------------------------------------------------------- /custom-events/site/index.html: -------------------------------------------------------------------------------- 1 |
[[HOST_IP]]2 | 3 | `[[HOST_IP]]`{{execute}} 4 | 5 |
[[HOST2_IP]]6 | 7 | `[[HOST2_IP]]`{{execute}} 8 | -------------------------------------------------------------------------------- /challenges/kubernetes-challenge-1/assets/tux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/challenges/kubernetes-challenge-1/assets/tux.png -------------------------------------------------------------------------------- /displaying-images/assets/logo-text-with-head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/displaying-images/assets/logo-text-with-head.png -------------------------------------------------------------------------------- /environment-usages/kubernetes-cluster-running/step1.md: -------------------------------------------------------------------------------- 1 | This environment has a `launch.sh`{{execute}} 2 | 3 | You can get with `kubectl get nodes`{{execute}} -------------------------------------------------------------------------------- /challenges/kubernetes-challenge-1/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/challenges/kubernetes-challenge-1/assets/.DS_Store -------------------------------------------------------------------------------- /environment-usages/python-3-7/step1.md: -------------------------------------------------------------------------------- 1 | # Python 3.7 time features example 2 | 3 | ```python 4 | import time 5 | 6 | print(time.time_ns()) 7 | ```{{execute T1}} 8 | -------------------------------------------------------------------------------- /run-commands-automatically/step3-foreground.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Waiting to complete"; while [ ! -f /opt/.backgroundfinished ] ; do sleep 2; done; echo "Done" -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/assets/streetcar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/challenges/challenge-linux-solver/assets/streetcar.pdf -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/assets/solutions.sh.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/challenges/challenge-linux-solver/assets/solutions.sh.enc -------------------------------------------------------------------------------- /displaying-progress-spinner/background.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sleep 15 3 | echo "done" >> /root/katacoda-finished 4 | sleep 15 5 | echo "done" >> /root/katacoda-background-finished -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/assets/cow-311266_640.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/challenges/challenge-linux-solver/assets/cow-311266_640.png -------------------------------------------------------------------------------- /challenges/node-challenge-1/finish.md: -------------------------------------------------------------------------------- 1 | This is the finish page for the challenge. 2 | 3 | See the documentation on the style guide for the recommended content of the finish pages. 4 | -------------------------------------------------------------------------------- /challenges/challenge-hello-world/finish.md: -------------------------------------------------------------------------------- 1 | This is the finish page for the challenge. 2 | 3 | See the documentation on the style guide for the recommended content of the finish pages. 4 | -------------------------------------------------------------------------------- /challenges/kubernetes-challenge-1/finish.md: -------------------------------------------------------------------------------- 1 | This is the finish page for the challenge. 2 | 3 | See the documentation on the style guide for the recommended content of the finish pages. 4 | -------------------------------------------------------------------------------- /instruction-guided-template/step2.md: -------------------------------------------------------------------------------- 1 | This will be the second step, and the test should catch the error in 2 | this command when the command fails: 3 | 4 | `ehco [[HOST_IP]]`{{execute}} 5 | -------------------------------------------------------------------------------- /run-commands-automatically/step3-background.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "This is a background script with a long running process" 4 | 5 | sleep 10 6 | 7 | echo "done" >> /opt/.backgroundfinished -------------------------------------------------------------------------------- /create-scenario-101/intro.md: -------------------------------------------------------------------------------- 1 | In this scenario, you will learn how to create your first Katacoda scenario. It will explain the essential aspects and the design decisions that you need to be aware of. 2 | -------------------------------------------------------------------------------- /automatedtest/step2.md: -------------------------------------------------------------------------------- 1 | This will be the second step. 2 | 3 | ## Run Commands 4 | 5 | Commands can have tags to save duplicating the command within the test. 6 | 7 | `uname -a`{{execute test-uname}} 8 | -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/assets/learning-modern-linux-book.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katacoda/scenario-examples/HEAD/challenges/challenge-linux-solver/assets/learning-modern-linux-book.jpg -------------------------------------------------------------------------------- /challenges/node-challenge-1/assets/cheat: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | type_out "Updating code to include return" 4 | 5 | cat << EOF > /root/multiply.js 6 | function multiply(a, b){ 7 | return a * b 8 | } 9 | EOF -------------------------------------------------------------------------------- /automatedtest/foreground.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "The IP address for this environment is [[HOST_IP]]" 4 | echo "The URL to access Port 80 is [[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]" 5 | -------------------------------------------------------------------------------- /custom-events/site/build-zip.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tar --exclude=dist/ --exclude=.git/ --exclude=.idea/ --exclude=build-zip.sh --exclude=.DS_Store --exclude=node_modules/ --exclude=css/ -zcvf site.tar.gz . 4 | -------------------------------------------------------------------------------- /instruction-guided-template/intro.md: -------------------------------------------------------------------------------- 1 | This is an example lab that will be tested using [instruction-guided testing](https://interactive-docs.oreilly.com/overview/automated-tests.html#instruction-guided-tests). 2 | -------------------------------------------------------------------------------- /verified-steps/step2-answer.md: -------------------------------------------------------------------------------- 1 | This allows you to display additional information to help the user with the scenario. 2 | 3 | Such as, the correct command the user needs to run: 4 | 5 | `git init`{{execute}} 6 | -------------------------------------------------------------------------------- /challenges/kubernetes-challenge-1/configure.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | launch.sh 4 | kubectl apply -f /opt/deploy.yaml 5 | rm /opt/deploy.yaml 6 | 7 | echo "Started" 8 | echo 'done' > /opt/katacoda-background-finished -------------------------------------------------------------------------------- /course-data-host01-reference/set-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | clear 4 | while [ ! -f /tmp/wait-script.sh ] 5 | do 6 | clear 7 | sleep .2 8 | done 9 | 10 | chmod 755 /tmp/wait-script.sh; /tmp/wait-script.sh -------------------------------------------------------------------------------- /environments/exampleenv1/build/4_configure_environment.sh: -------------------------------------------------------------------------------- 1 | cat << EOF > /opt/configure-environment.sh 2 | #!/bin/bash 3 | 4 | docker run -it -d --name=http -p 80:80 nginx 5 | EOF 6 | chmod +x /opt/configure-environment.sh -------------------------------------------------------------------------------- /custom-events/intro.md: -------------------------------------------------------------------------------- 1 | Katacoda provides a service that listens to events defined in the scenario and allows to override the default behavior of the platform. 2 | 3 | This scenario will show you how to use custom events. 4 | -------------------------------------------------------------------------------- /environment-usages/minikube/step1.md: -------------------------------------------------------------------------------- 1 | The Minikube environment is designed for teaching users how to deploy Kubernetes using the Minikube project. 2 | 3 | The cluster can be started with the command `minikube start`{{execute}}. -------------------------------------------------------------------------------- /instruction-guided-template/foreground.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "The IP address for this environment is [[HOST_IP]]" 4 | echo "The URL to access Port 80 is [[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]" 5 | -------------------------------------------------------------------------------- /use-ip-or-dns-for-environment/foreground.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "The IP address for this environment is [[HOST_IP]]" 4 | echo "The URL to access Port 80 is [[HOST_SxeUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]" 5 | -------------------------------------------------------------------------------- /credit-where-credit-due/finish.md: -------------------------------------------------------------------------------- 1 | You have learned about credits. You can also consider adding to the bottom of the finish page a way for the learners to context you. 2 | 3 | --- 4 | 5 | [Contact the author](https://www.github.com) -------------------------------------------------------------------------------- /create-scenario-101/finish.md: -------------------------------------------------------------------------------- 1 | Congratulations. You have completed the scenario on the foundations of creating Katacoda content. Continue looking at examples on [katacoda.com/scenario-examples](https://katacoda.com/scenario-examples) 2 | -------------------------------------------------------------------------------- /environment-usages/kubernetes-cluster/step2.md: -------------------------------------------------------------------------------- 1 | Copy the command from the `kubeadm init` output starting with `kubeadm join` 2 | 3 | Run this on the second node, this will automatically add the node. Verify with `kubectl get nodes`{{execute}} -------------------------------------------------------------------------------- /displaying-progress-spinner/intro.md: -------------------------------------------------------------------------------- 1 | To indicate action happening in the background, a progress spinner can help provide uses with helpful feedback. 2 | 3 | This example is waiting for two background scripts to finish, each taking 15 seconds. 4 | -------------------------------------------------------------------------------- /custom-events/site/server/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | @@include('./index.html') 8 | 9 | 10 | -------------------------------------------------------------------------------- /course-data-host01-reference/step1.md: -------------------------------------------------------------------------------- 1 | This scenario contains an explicit reference to host01 in the background script (init-env.sh) 2 | 3 | ```sh 4 | sleep 2 5 | ssh root@host01 "chmod 755 /tmp/load-quiz.sh; /tmp/load-quiz.sh > /tmp/load-quiz.out" 6 | ``` 7 | -------------------------------------------------------------------------------- /environment-usages/ubuntu-2004/finish.md: -------------------------------------------------------------------------------- 1 | Start using the Ubuntu environment for your content by setting the `imageid` to `ubuntu:2004`. 2 | 3 | For example: 4 | 5 |
6 | "backend": {
7 | "imageid": "ubuntu:2004"
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/task_1.md:
--------------------------------------------------------------------------------
1 |
2 | Use `cowsay` with the quote:
3 |
4 | `O'Reilly, inspiring the future for more than 40 years`
5 |
6 | Steer this bovonic output to a file named `cowsay.txt`.
7 |
--------------------------------------------------------------------------------
/upload-assets/background.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | sleep 5
4 |
5 | echo "done" | sudo tee /root/katacoda-finished
6 |
7 | echo "Everything ready... Finalise the deployment"
8 |
9 | /usr/local/bin/deploy.sh
10 |
11 | echo "done" | sudo tee /root/katacoda-background-finished
--------------------------------------------------------------------------------
/automatedtest/step1.md:
--------------------------------------------------------------------------------
1 | This is an example scenario that will be tested using the Cypress integration.
2 |
3 | ## IP Address
4 |
5 | `echo [[HOST_IP]]`{{execute test-echoIP}}
6 |
7 | ## URL
8 |
9 | `echo [[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]`{{execute}}
10 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/verify.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | task=$(solver status -q)
4 | solver verify -q "$task" >> "/tmp/solver.out"
5 |
6 | verify_error=$?
7 | if [[ $verify_error -eq 0 ]]
8 | then
9 | solver request_advance_task "$task"
10 | fi
11 | exit $verify_error
12 |
--------------------------------------------------------------------------------
/create-course/intro.md:
--------------------------------------------------------------------------------
1 | In this scenario, you will learn how to create a course, a series of Katacoda scenarios collected together.
2 |
3 | The advantage of creating a course is the ability to group related scenarios to provide users with a clear explanation and order to complete the content.
--------------------------------------------------------------------------------
/instruction-guided-template/step1.md:
--------------------------------------------------------------------------------
1 | The instruction-guided tests are located in
2 | `.cypress/instruction_guided_spec.js`.
3 |
4 | ## IP Address
5 |
6 | `echo [[HOST_IP]]`{{execute}}
7 |
8 | ## URL
9 |
10 | `echo [[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]`{{execute}}
11 |
--------------------------------------------------------------------------------
/environment-usages/minikube/finish.md:
--------------------------------------------------------------------------------
1 | Start using the Minikube environment for your content by setting the `imageid` to `minikube`.
2 |
3 | For example:
4 |
5 |
6 | "environment": {
7 | "uilayout": "terminal"
8 | },
9 | "backend": {
10 | "imageid": "minikube"
11 | }
12 |
--------------------------------------------------------------------------------
/challenges/kubernetes-challenge-1/1_task.md:
--------------------------------------------------------------------------------
1 | Write the environment variables from the application environment to a file called `/root/app-env`
2 |
3 | ---
4 |
5 |
6 | Here is an example of placing an image in the markdown of the task.
7 |
--------------------------------------------------------------------------------
/environment-usages/ubuntu-1804/finish.md:
--------------------------------------------------------------------------------
1 | Start using the Ubuntu environment for your content by setting the `imageid` to `ubuntu:1804`.
2 |
3 | For example:
4 |
5 |
6 | "backend": {
7 | "imageid": "ubuntu:1804"
8 | }
9 |
10 |
11 | Looking for Ubuntu 20.04? Use the imageid `ubuntu:2004`
--------------------------------------------------------------------------------
/strings.yaml:
--------------------------------------------------------------------------------
1 | # Override the strings in the UI
2 | continue: "Continue"
3 | start_scenario: "Start Scenario"
4 | welcome: "Welcome"
5 | difficulty: "Difficuly"
6 | time: "Time"
7 | steps: "Steps"
8 | step: "Step"
9 | of: "of"
10 | answer: "Answer"
11 | solution: "Solution"
12 | show: "Show"
13 | terminal: "Terminal"
14 |
--------------------------------------------------------------------------------
/opening-multiple-terminals/step1.md:
--------------------------------------------------------------------------------
1 | `echo "Run in T1"`{{execute T1}}
2 |
3 | `echo "Run in T1"`{{execute T1}}
4 |
5 | `echo "Run in T2"`{{execute T2}}
6 |
7 | `echo "Run in T2"`{{execute T2}}
8 |
9 | `echo "No Terminal Defined"`{{execute}}
10 |
11 | `echo "No Terminal Defined"`{{execute}}
12 |
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/intro.md:
--------------------------------------------------------------------------------
1 | This is a simplified "Hello, world" example for a challenge. It uses the challenge format v0.8, and illustrates how to define:
2 |
3 | - tasks
4 | - verification tests
5 | - hints
6 |
7 | Please reference the [detailed challenge documentation here](https://www.katacoda.community/challenges/challenges.html).
8 |
--------------------------------------------------------------------------------
/challenges/kubernetes-challenge-1/1_hint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | seconds_sofar=$1
4 |
5 | if [[ $seconds_sofar -ge 5 && $seconds_sofar -lt 10 ]]; then
6 | echo "Keep going, a hint will be shown soon..."
7 | fi
8 |
9 |
10 | if [ $seconds_sofar -ge 10 ]; then
11 | echo "Hint: try running the command:"
12 | echo "curl node01:30080"
13 | fi
--------------------------------------------------------------------------------
/vscode-extensions/foreground.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | EXTENSION_DIR=/opt/.katacodacode/extensions/humao.rest-client-0.24.3
3 | while ! [ -d "$EXTENSION_DIR" ];
4 | do
5 | clear;
6 | echo 'Installing VS Code extensions, please wait before opening the IDE'.
7 | sleep 1
8 | done
9 | clear
10 | : VS Code extensions installed, now you can open the IDE
--------------------------------------------------------------------------------
/environment-usages/kubernetes-cluster/finish.md:
--------------------------------------------------------------------------------
1 | Start using the Multi-node Kubernetes environment for your content by setting the `imageid` to `kubernetes-cluster:1.18`.
2 |
3 | For example:
4 |
5 |
6 | "environment": {
7 | "uilayout": "terminal"
8 | },
9 | "backend": {
10 | "imageid": "kubernetes-cluster:1.18"
11 | }
12 |
--------------------------------------------------------------------------------
/custom-events/env-init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | curl -L https://github.com/katacoda/scenario-examples/blob/master/custom-events/site/site.tar.gz?raw=true > ~/site.tar.gz
4 | tar -xvzf ~/site.tar.gz -C /home/scrapbook/tutorial
5 | cd /home/scrapbook/tutorial
6 | npm install
7 | echo "Starting... this will block the rest of the commands from running..."
8 | npm start
9 |
--------------------------------------------------------------------------------
/environment-usages/kubernetes-cluster-running/finish.md:
--------------------------------------------------------------------------------
1 | Start using the Multi-node Kubernetes environment for your content by setting the `imageid` to `kubernetes-cluster-running:1.18`.
2 |
3 | For example:
4 |
5 |
6 | "environment": {
7 | "uilayout": "terminal"
8 | },
9 | "backend": {
10 | "imageid": "kubernetes-cluster-running:1.18"
11 | }
12 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/hint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # First parameter, $1, is time in seconds since task instructions were presented
4 |
5 | task=$(solver status -q)
6 | solver verify "$task" -q > /dev/null 2>&1
7 |
8 | # Error code returned from challenge_verify is the hint content id to present
9 | verify_error=$?
10 | solver request_hint "$1" "$task" "$verify_error"
11 |
--------------------------------------------------------------------------------
/environment-usages/kubernetes-cluster/step1.md:
--------------------------------------------------------------------------------
1 | With this environment the Kubernetes nodes are not configured. If you want to configure the nodes then you'd need to run `kubeadm` which has been set and configured. For example, for following command will initialise the master with the latest version installed.
2 |
3 | `kubeadm init --kubernetes-version $(kubeadm version -o short)`{{execute HOST1}}
--------------------------------------------------------------------------------
/displaying-images/step1.md:
--------------------------------------------------------------------------------
1 | Within the Assets directory, images can be embedded into the scenarios.
2 |
3 | In this scenario, within the Assets directory, a `logo-text-with-head.png` file exists. Only images within the Assets directory are available.
4 |
5 | 6 |  7 |8 | 9 |  10 | -------------------------------------------------------------------------------- /uilayouts/uilayout-iframe/step1.md: -------------------------------------------------------------------------------- 1 | The Katacoda `iframe` UI Layout provides a full webpage experience. 2 | 3 | # Index.json 4 | 5 | Example: 6 | 7 |
8 | "environment": {
9 | "uilayout": "iframe"
10 | },
11 | "backend": {
12 | "url": "https://httpbin.org"
13 | }
14 |
15 |
16 | ## Important
17 |
18 | The URL must be HTTPS and not block being embedded via an iframe.
19 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/task_2.md:
--------------------------------------------------------------------------------
1 | Sometimes text output can be messy and hard to decipher. Take a look at the standard Linux `passwd` file:
2 |
3 | `cat /etc/passwd`{{execute}}.
4 |
5 | It's would be kinder to the eyes to format all the data in the rows into a table with columns.
6 |
7 | Find the right Linux command to tabularize the contents of the `passwd` file into the file `kinder-passwd.txt`.
8 |
--------------------------------------------------------------------------------
/.dev/README.md:
--------------------------------------------------------------------------------
1 | # Development
2 |
3 | ## Available Make targets
4 |
5 | List all available Make targets with information.
6 |
7 | ```bash
8 | make help
9 | ```
10 |
11 | ## Development environment
12 |
13 | Start docker container with development enviroment.
14 |
15 | ```bash
16 | make bash
17 | ```
18 |
19 | ## Validate
20 |
21 | Validate the code.
22 |
23 | ```bash
24 | make validate
25 | ```
26 |
--------------------------------------------------------------------------------
/environment-usages/ubuntu-1804/step1.md:
--------------------------------------------------------------------------------
1 | The Katacoda Ubuntu Environment is a flexible single node environment. Users are connected to the environment as `root`. This provides them with full flexibility to install additional packages, explore the internals of Linux and experiment with new ideas.
2 |
3 | The environment also comes with Docker, meaning additional containers can be pulled and access as and when required.
4 |
--------------------------------------------------------------------------------
/verified-steps/step2.md:
--------------------------------------------------------------------------------
1 | To provide users with more information or context, a Answer section can be provided.
2 |
3 | Here is the snippet from Step 2 within the `index.json`.
4 |
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Show Answers",
9 | "text": "step2.md",
10 | "answer": "step2-answer.md"
11 | }
12 | ]
13 | }
14 |
15 |
--------------------------------------------------------------------------------
/environment-usages/ubuntu-2004/step1.md:
--------------------------------------------------------------------------------
1 | The Katacoda Ubuntu Environment is a flexible single node environment. Users are connected to the environment as `root`. This provides them with full flexibility to install additional packages, explore the internals of Linux and experiment with new ideas.
2 |
3 | The environment also comes with Docker, meaning additional containers can be pulled and access as and when required.
4 |
5 |
--------------------------------------------------------------------------------
/challenges/node-challenge-1/background.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Started"
4 |
5 | cat << 'EOF' > /root/multiply.js
6 | function multiply(a, b){
7 | a * b
8 | }
9 | EOF
10 |
11 | cat << 'EOF' > /opt/test.js
12 | var x = multiply(2, 2);
13 | if(x === 4) {
14 | process.exit(0);
15 | } else {
16 | console.log("Error", x);
17 | process.exit(1);
18 | }
19 | EOF
20 |
21 | echo 'done' > /opt/katacoda-background-finished
--------------------------------------------------------------------------------
/create-scenario-101/step4.md:
--------------------------------------------------------------------------------
1 | For those who prefer an IDE based extension, Katacoda has extended Visual Studio Code for scenario management.
2 |
3 | This can be installed from the [Katacoda page on the Visual Studio Marketplace](https://marketplace.visualstudio.com/items/Katacoda.vscode/).
4 |
5 | After installation, you can create new scenarios, add steps and have quick access to the Katacoda markdown features via snippets.
6 |
--------------------------------------------------------------------------------
/vscode-extensions/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections
3 | cd /tmp && apt-get update -y && apt install libarchive-tools -y # install bsdtar
4 | curl -L https://marketplace.visualstudio.com/_apis/public/gallery/publishers/humao/vsextensions/rest-client/0.24.3/vspackage | bsdtar -xvf - extension
5 | mv extension /opt/.katacodacode/extensions/humao.rest-client-0.24.3
--------------------------------------------------------------------------------
/delivering-training-pathway.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Delivering Training with Katacoda",
3 | "description": "",
4 | "icon": "fa-katacoda",
5 | "courses": [
6 | {
7 | "course_id": "creating-training-pathway-json",
8 | "title": "Creating Training Pathway.json File",
9 | "description": "Learn how to define the scenarios to deliver for training"
10 | }
11 | ]
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/quiz/intro.md:
--------------------------------------------------------------------------------
1 | The purpose of the **Interactive Quiz** is to help retain knowledge discussed within the scenario and highlight the key topics you want users to remember.
2 |
3 | Within the quiz, users have complete access to the terminal environment. This allows them to explore the environment to identify the correct answer in order to proceed.
4 |
5 | Users need to answer all the questions correctly to get proceed to the next step.
--------------------------------------------------------------------------------
/use-ip-or-dns-for-environment/step1.md:
--------------------------------------------------------------------------------
1 | ## IP Address
2 |
3 | `echo [[HOST_IP]]`{{execute}}
4 |
5 | [[HOST_IP]]6 | 7 | ## URL 8 | 9 | `echo [[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]`{{execute}} 10 | 11 |
[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].[[KATACODA_DOMAIN]]12 | 13 |
[[HOST_SUBDOMAIN]]14 | 15 |
[[KATACODA_HOST]]16 | 17 |
[[KATACODA_DOMAIN]]18 | -------------------------------------------------------------------------------- /custom-events/site/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "new-app", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "gulp default" 7 | }, 8 | "dependencies": { 9 | "fs": "0.0.1-security", 10 | "request": "^2.88.0" 11 | }, 12 | "devDependencies": { 13 | "browser-sync": "^2.26.7", 14 | "gulp": "^4.0.2", 15 | "gulp-file-include": "^2.1.0", 16 | "gulp-sass": "^4.0.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /uilayouts/uilayout-terminal/step1.md: -------------------------------------------------------------------------------- 1 | The Katacoda `terminal` UI Layout provides a full Terminal experience. 2 | 3 | # Index.json 4 | 5 | Example: 6 | 7 |
8 | "environment": {
9 | "uilayout": "terminal"
10 | },
11 |
12 |
13 | # Helper Functionality
14 |
15 | `echo "Running a command"`{{execute}}
16 |
17 | Other Terminal and Markdown functionality is available at https://katacoda.com/scenario-examples/scenarios/markdown-extensions
--------------------------------------------------------------------------------
/environment-usages/go/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Go Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Go",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "golang:1.14"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/ruby/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Ruby Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Ruby",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "ruby:2.5"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/bash/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Bash Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Bash",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "bash1804"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/environment-usages/java/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Java Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Java",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "openjdk:15"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/r/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "R Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "R Language",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "rlang:3.6.3"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/rust/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Rust Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Rust",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "rust:1.44.1"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/dotnet/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": ".NET Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": ".NET",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "dotnet:6.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/environment-usages/kotlin/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Kotlin Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Kotlin",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "kotlin:1.3.61"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/nodejs/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Node.js Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Node.js",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "nodejs:12"
21 | }
22 | }
--------------------------------------------------------------------------------
/environment-usages/python/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Python 3 Environment",
3 | "description": "",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Python",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal"
18 | },
19 | "backend": {
20 | "imageid": "python:3.6"
21 | }
22 | }
--------------------------------------------------------------------------------
/create-scenario-101/step5.md:
--------------------------------------------------------------------------------
1 | As changes are being made to the content, when they are pushed into the master branch of a Git repository, the content on Katacoda can automatically be updated via a webhook.
2 |
3 | To configure the Webhook, follow the guide at [Create Author Profile](https://www.katacoda.community/essentials/author-profile.html).
4 |
5 | Once the webhook is installed, everytime you make a push to update to your content, Katacoda will be automatically updated.
6 |
--------------------------------------------------------------------------------
/placeholders/step2.md:
--------------------------------------------------------------------------------
1 | Render port 8500: https://[[HOST_SUBDOMAIN]]-8500-[[KATACODA_HOST]].environments.katacoda.com/
2 |
3 | Render port 8500: https://[[HOST_SUBDOMAIN]]-8500-[[KATACODA_HOST]].environments.katacoda.com/4 | 5 | Render port 80:
https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com/6 | 7 | Display page allowing user to select port: 8 |
https://[[HOST_SUBDOMAIN]]-[[KATACODA_HOST]].environments.katacoda.com/9 | -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/task_4.md: -------------------------------------------------------------------------------- 1 | The [xargs](https://man7.org/linux/man-pages/man1/xargs.1.html) command is powerful in Linux. Given the set of "1 2 3 4 5", with just one line add the string "I am [n]" to each file [n].txt. For instance file `2.txt` should contain a single line with "I am 2", file `5.txt` will contain "I am 5". This challenge needs to see your command, so place it in the provided script file `my-xargs.sh`. 2 | 3 | 💡 Tip: do not use a `for` or `while` loop, just xargs. 4 | -------------------------------------------------------------------------------- /environment-usages/minikube/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Minikube", 3 | "description": "Use Minikube to help explain Kubernetes", 4 | "details": { 5 | "steps": [ 6 | { 7 | "title": "Start Minikube", 8 | "text": "step1.md" 9 | } 10 | ], 11 | "finish": { 12 | "text": "finish.md" 13 | } 14 | }, 15 | "environment": { 16 | "hideintro": true, 17 | "uilayout": "terminal" 18 | }, 19 | "backend": { 20 | "imageid": "minikube" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /environment-usages/ubuntu-1804/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Ubuntu 18.04 Instance", 3 | "description": "General purpose environment", 4 | "details": { 5 | "steps": [ 6 | { 7 | "title": "Ubuntu 18.04", 8 | "text": "step1.md" 9 | } 10 | ], 11 | "finish": { 12 | "text": "finish.md" 13 | } 14 | }, 15 | "environment": { 16 | "hideintro": true, 17 | "uilayout": "terminal" 18 | }, 19 | "backend": { 20 | "imageid": "ubuntu:1804" 21 | } 22 | } -------------------------------------------------------------------------------- /environment-usages/ubuntu-2004/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Ubuntu 20.04 Instance", 3 | "description": "General purpose environment", 4 | "details": { 5 | "steps": [ 6 | { 7 | "title": "Ubuntu 20.04", 8 | "text": "step1.md" 9 | } 10 | ], 11 | "finish": { 12 | "text": "finish.md" 13 | } 14 | }, 15 | "environment": { 16 | "hideintro": true, 17 | "uilayout": "terminal" 18 | }, 19 | "backend": { 20 | "imageid": "ubuntu:2004" 21 | } 22 | } -------------------------------------------------------------------------------- /environment-usages/python-3-7/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Python 3.7 Environment", 3 | "description": "", 4 | "details": { 5 | "steps": [ 6 | { 7 | "title": "Python", 8 | "text": "step1.md", 9 | "foreground": "foreground.sh" 10 | } 11 | ], 12 | "finish": { 13 | "text": "finish.md" 14 | } 15 | }, 16 | "environment": { 17 | "hideintro": true, 18 | "uilayout": "terminal" 19 | }, 20 | "backend": { 21 | "imageid": "python:3.7" 22 | } 23 | } -------------------------------------------------------------------------------- /environment-usages/python-3-8/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Python 3.8 Environment", 3 | "description": "", 4 | "details": { 5 | "steps": [ 6 | { 7 | "title": "Python", 8 | "text": "step1.md", 9 | "foreground": "foreground.sh" 10 | } 11 | ], 12 | "finish": { 13 | "text": "finish.md" 14 | } 15 | }, 16 | "environment": { 17 | "hideintro": true, 18 | "uilayout": "terminal" 19 | }, 20 | "backend": { 21 | "imageid": "python:3.8" 22 | } 23 | } -------------------------------------------------------------------------------- /environments/kc-custom-env-vscode-extensions/build/1_init.sh: -------------------------------------------------------------------------------- 1 | cd /tmp && apt-get update -y && apt install libarchive-tools -y # install bsdtar 2 | curl -L https://marketplace.visualstudio.com/_apis/public/gallery/publishers/ms-python/vsextensions/python/2020.5.86806/vspackage | bsdtar -xvf - extension 3 | # The name of the directory for the extension should use the same pattern that when the extension is installed manually
6 | {
7 | "pathway_id" "environment-usages",
8 | "course_id": "ubuntu",
9 | "title": "Ubuntu 16.04 Instance",
10 | "description": "General purpose environment"
11 | },
12 |
13 |
14 | This would reference the scenario Ubuntu from the environment-usages course.
15 |
--------------------------------------------------------------------------------
/create-scenario-101/step2.md:
--------------------------------------------------------------------------------
1 | Within each step, a series of Markdown extensions have been created that can provide various details for the users' experience.
2 |
3 | For example, commands such as `echo "Run in Terminal"`{{execute}} can be executed by clicking the command.
4 |
5 | This is done by adding `execute` to the markdown code block, for example:
6 | `echo "Run in Terminal"`{{execute}}
7 |
8 | More can be found at the scenario [on Markdown extensions](https://katacoda.com/scenario-examples/scenarios/markdown-extensions).
9 |
--------------------------------------------------------------------------------
/writing-files/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Writing files",
4 | "description": "How to write a file in your scenario",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "How to write a file in your scenario",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "uilayout": "terminal"
16 | },
17 | "backend": {
18 | "imageid": "ubuntu"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/clipboard/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Clipboard Integration",
4 | "description": "Understand how the Katacoda Clipboard integration",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Clipboard Examples",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "uilayout": "editor-terminal"
16 | },
17 | "backend": {
18 | "imageid": "ubuntu"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Terminal UI",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "hidefinish": true,
16 | "uilayout": "terminal"
17 | },
18 | "backend": {
19 | "imageid": "nodejs:12"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/environments/README.md:
--------------------------------------------------------------------------------
1 | ## Creating Custom Environments on Katacoda
2 |
3 | NOTE: This is only available with a Katacoda Subscription.
4 |
5 | ## Creating a new Environment
6 |
7 | 1) Create a directory. This will be the name of the environment.
8 |
9 | 2) Within the directory, create a new directory called build
10 |
11 | 3) Within the build directory, create a series of bash scripts, prefixed with a number to indicate order. For example:
12 |
13 | 1_installpackages.sh
14 |
15 | 2_pulldockerimages.sh
16 |
17 | 3_pulldemos.sh
18 |
--------------------------------------------------------------------------------
/markdown-extensions/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Markdown Extensions",
4 | "description": "Examples of how the Katacoda Markdown Extensions",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Extension Examples",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "uilayout": "terminal-terminal"
16 | },
17 | "backend": {
18 | "imageid": "docker-swarm"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal-terminal/step1.md:
--------------------------------------------------------------------------------
1 | The Katacoda `terminal-terminal` UI Layout provides a full Terminal experience.
2 |
3 | # Index.json
4 |
5 | Example:
6 |
7 |
8 | "environment": {
9 | "uilayout": "terminal-terminal"
10 | },
11 |
12 |
13 | # Helper Functionality
14 |
15 | `echo "Running a command on Host 1"`{{execute HOST1}}
16 |
17 | `echo "Running a command on Host 2"`{{execute HOST2}}
18 |
19 | Other Terminal and Markdown functionality is available at https://katacoda.com/scenario-examples/scenarios/markdown-extensions
20 |
--------------------------------------------------------------------------------
/environment-usages/kubernetes-cluster-running/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Kubernetes",
3 | "description": "Multi node Kubernetes cluster configured using Kubeadm",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Running Kubernetes",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "finish": {
12 | "text": "finish.md"
13 | }
14 | },
15 | "environment": {
16 | "hideintro": true,
17 | "uilayout": "terminal-terminal"
18 | },
19 | "backend": {
20 | "imageid": "kubernetes-cluster-running:1.18"
21 | }
22 | }
--------------------------------------------------------------------------------
/run-commands-automatically/step2.md:
--------------------------------------------------------------------------------
1 | By having scripts run for each step it's possible to simulate activities and actions under the covers. Such as modifying settings or causing additional situations for the user to complete.
2 |
3 | The syntax is the same as when defining scripts for intro.
4 |
5 |
6 | {
7 | "title": "Scripts Example",
8 | "courseData": "step2-background.sh",
9 | "code": "step2-foreground.sh",
10 | "text": "step2.md"
11 | }
12 |
13 |
14 | The next step will explain how to handle the user having to wait for a task to complete.
15 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal-terminal/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Terminal Terminal UI",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "hidefinish": true,
16 | "uilayout": "terminal-terminal"
17 | },
18 | "backend": {
19 | "imageid": "docker-swarm"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-iframe/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Terminal iFrame UI",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "hidefinish": true,
16 | "uilayout": "iframe"
17 | },
18 | "backend": {
19 | "imageid": "bash",
20 | "url": "https://httpbin.org"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/embed-videos/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Embed Videos into Scenarios",
3 | "description": "Learn how to embed YouTube videos into a scenario",
4 | "difficulty": "Beginner",
5 | "time": "10",
6 | "details": {
7 | "steps": [
8 | {
9 | "title": "Embedding Video",
10 | "text": "step1.md"
11 | }
12 | ],
13 | "intro": {
14 | "text": "intro.md"
15 | },
16 | "finish": {
17 | "text": "finish.md"
18 | }
19 | },
20 | "environment": {
21 | "uilayout": "terminal"
22 | },
23 | "backend": {
24 | "imageid": "bash"
25 | }
26 | }
--------------------------------------------------------------------------------
/automatedtest/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Automated Test Example",
3 | "description": "Example of using Cypress to test a Katacoda scenario",
4 | "details": {
5 | "steps": [
6 | {
7 | "text": "step1.md",
8 | "code": "foreground.sh"
9 | },
10 | {
11 | "text": "step2.md"
12 | }
13 | ],
14 | "intro": {
15 | "text": "intro.md"
16 | },
17 | "finish": {
18 | "text": "finish.md"
19 | }
20 | },
21 | "environment": {
22 | "uilayout": "terminal"
23 | },
24 | "backend": {
25 | "imageid": "ubuntu"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/delivering-training/creating-training-pathway-json/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Creating Training Pathway.json File",
3 | "description": "Learn how to define the scenarios to deliver for training",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "training-pathway.json",
8 | "text": "step1.md"
9 | }
10 | ],
11 | "intro": {
12 | "text": "intro.md"
13 | },
14 | "finish": {
15 | "text": "finish.md"
16 | }
17 | },
18 | "environment": {
19 | "uilayout": "editor-terminal"
20 | },
21 | "backend": {
22 | "imageid": "ubuntu"
23 | }
24 | }
--------------------------------------------------------------------------------
/instruction-guided-template/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Instruction-Guided Test Template",
3 | "description": "Example of using Cypress to test a lab",
4 | "details": {
5 | "steps": [
6 | {
7 | "text": "step1.md",
8 | "code": "foreground.sh"
9 | },
10 | {
11 | "text": "step2.md"
12 | }
13 | ],
14 | "intro": {
15 | "text": "intro.md"
16 | },
17 | "finish": {
18 | "text": "finish.md"
19 | }
20 | },
21 | "environment": {
22 | "uilayout": "terminal"
23 | },
24 | "backend": {
25 | "imageid": "ubuntu"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal-iframe-split/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Terminal iFrame Split UI",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "hidefinish": true,
16 | "uilayout": "terminal-iframe-split"
17 | },
18 | "backend": {
19 | "imageid": "ubuntu:2004",
20 | "port": 80
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/visualise-docker/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Visualise Environment Tab",
4 | "description": "Learn how to add the Visualise Tab",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "files": [],
14 | "environment": {
15 | "hideintro": true, "hidefinish": true,
16 |
17 | "showvisualise": true,
18 |
19 | "uilayout": "terminal"
20 | },
21 | "backend": {
22 | "imageid": "ubuntu"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/displaying-web-ui/step1.md:
--------------------------------------------------------------------------------
1 | ## Start HTTP Server
2 |
3 | `docker container run -p 80:80 -d katacoda/docker-http-server`{{execute}}
4 |
5 | ## Test
6 |
7 | `curl localhost`{{execute}}
8 |
9 | ## Generated Web Link
10 |
11 | https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com
12 |
13 | ## Markdown
14 |
15 | https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com16 | 17 | ## Learn More 18 | 19 | [Displaying Tabs](https://katacoda.com/scenario-examples/scenarios/dashboard-tabs) and [embedding iFrames](https://katacoda.com/scenario-examples/scenarios/dashboard-tabs-iframe) 20 | -------------------------------------------------------------------------------- /writing-files/step1.md: -------------------------------------------------------------------------------- 1 | You can write files in your scenario using a bash command: 2 | 3 | ```sh 4 | cat << EOF > /tmp/storageos-secret.yaml 5 | apiVersion: v1 6 | kind: Secret 7 | metadata: 8 | name: "storageos-api" 9 | namespace: "storageos-operator" 10 | labels: 11 | app: "storageos" 12 | type: "kubernetes.io/storageos" 13 | EOF 14 | ```{{execute}} 15 | 16 | Check the content of your file 17 | `cat /tmp/storageos-secret.yaml`{{execute}} 18 | 19 | If you need to have those files created at the start of the scenario, you can define them as [assets](https://katacoda.com/scenario-examples/scenarios/upload-assets). 20 | -------------------------------------------------------------------------------- /opening-multiple-terminals/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Opening Multiple Terminals", 3 | "description": "Show how to create multiple terminals when running commands", 4 | "difficulty": "Beginner", 5 | "time": "10 minutes", 6 | "details": { 7 | "steps": [ 8 | { 9 | "title": "Multiple Terminals", 10 | "text": "step1.md" 11 | } 12 | ], 13 | "intro": { 14 | "text": "intro.md" 15 | }, 16 | "finish": { 17 | "text": "finish.md" 18 | } 19 | }, 20 | "environment": { 21 | "uilayout": "terminal" 22 | }, 23 | "backend": { 24 | "imageid": "bash" 25 | } 26 | } -------------------------------------------------------------------------------- /uilayouts/uilayout-terminal-iframe-split/step1.md: -------------------------------------------------------------------------------- 1 | The Katacoda `terminal-iframe-split` UI Layout provides a full Terminal experience with the ability to include webpages as iframes alongside the content. 2 | 3 | # Index.json 4 | 5 | Example: 6 | 7 |
8 | "environment": {
9 | "uilayout": "terminal-iframe-split"
10 | },
11 | "backend": {
12 | "imageid": "ubuntu",
13 | "port": 80
14 | }
15 |
16 |
17 | More information can be found at https://katacoda.com/scenario-examples/scenarios/dashboard-tabs-iframe
18 |
19 | # Example
20 |
21 | `docker container run -p 80:80 -d katacoda/docker-http-server`{{execute}}
22 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal-iframe/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Terminal iFrame UI",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "hideintro": true,
15 | "hidefinish": true,
16 | "showdashboards": true,
17 | "uilayout": "terminal-iframe"
18 | },
19 | "backend": {
20 | "imageid": "nodejs:12",
21 | "port": 3000
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-vscode-terminal/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with VS Code / Terminal UI Split Screen",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "text": "step1.md"
9 | }
10 | ],
11 | "intro": {
12 | "text": "intro.md",
13 | "credits": ""
14 | }
15 | },
16 | "environment": {
17 | "hidefinish": true,
18 | "uilayout": "vscode-terminal-split"
19 | },
20 | "backend": {
21 | "imageid": "nodejs:12"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/environment-usages/kubernetes-cluster/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Kubernetes",
3 | "description": "Multi node Kubernetes cluster configured using Kubeadm",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Control Plane Node",
8 | "text": "step1.md"
9 | },
10 | {
11 | "title": "Worker Node",
12 | "text": "step2.md"
13 | }
14 | ],
15 | "finish": {
16 | "text": "finish.md"
17 | }
18 | },
19 | "environment": {
20 | "hideintro": true,
21 | "uilayout": "terminal-terminal"
22 | },
23 | "backend": {
24 | "imageid": "kubernetes-cluster:1.18"
25 | }
26 | }
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/2_hint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # See hint documentation: https://www.katacoda.community/challenges.html#hints
4 |
5 | seconds_sofar=$1
6 |
7 | # echo "Debug Hint Task 2: $seconds_sofar"
8 |
9 | # This hint message will appear between 10-20 seconds elapsed after the task began.
10 | if [[ $seconds_sofar -ge 10 && $seconds_sofar -lt 20 ]]; then
11 | echo "Still working on it? I'll show a hint very soon..."
12 | fi
13 |
14 | # This hint message will appear 20+ seconds elapsed after the task began.
15 | if [ $seconds_sofar -ge 20 ]; then
16 | echo "**Hint:** This hint could nudge you toward the solution."
17 | fi
18 |
--------------------------------------------------------------------------------
/course-data-host01-reference/assets/wait-script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | clear
3 |
4 | echo -e "Preparing the environment ..."
5 |
6 | show_progress()
7 | {
8 | echo -n "Starting"
9 | local -r pid="${1}"
10 | local -r delay='0.75'
11 | local spinstr='\|/-'
12 | local temp
13 | printf " \b\b\b\b"
14 | until [ -f /tmp/kc-scenario-done ]; do
15 | temp="${spinstr#?}"
16 | printf " [%c] " "${spinstr}"
17 | spinstr=${temp}${spinstr%"${temp}"}
18 | sleep "${delay}"
19 | printf "\b\b\b\b\b\b"
20 | done
21 | printf " \b\b\b\b"
22 | echo ""
23 | echo "Configured"
24 | }
25 |
26 | show_progress
27 |
28 |
29 |
--------------------------------------------------------------------------------
/.dev/Makefile:
--------------------------------------------------------------------------------
1 | SHELL := /bin/bash
2 | .DEFAULT_GOAL := help
3 |
4 | .PHONY: help
5 | help: ## help target to show available commands with information
6 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
7 |
8 | .PHONY: validate
9 | validate: docker-build ## Validate the katacoda scenarios
10 | docker-compose run dev katacoda validate:all --repo=/app/
11 |
12 | .PHONY: docker-build
13 | docker-build: ## Build Container
14 | docker-compose build
15 |
16 | .PHONY: bash
17 | bash: docker-build ## Open bash with dev dependencies installed
18 | docker-compose run dev bash
19 |
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/SOLUTIONS.md:
--------------------------------------------------------------------------------
1 | # SOLUTIONS
2 |
3 | For each task, please provide instructions for the solution. This is NOT shared with the learner, but is for our internal reference, to help test the scenario.
4 |
5 | ## Task 1 Solution
6 |
7 | - Poke the widget
8 | - Change the value
9 | - Paste in the code
10 | - Run the thing
11 | - [etc...]
12 |
13 | ## Task 2 Solution
14 |
15 | - xxxxx
16 | - xxxxx
17 | - xxxxx
18 |
19 | ## Task 3 Solution
20 |
21 | - xxxxx
22 | - xxxxx
23 | - xxxxx
24 |
25 | ## Task 4 Solution
26 |
27 | - xxxxx
28 | - xxxxx
29 | - xxxxx
30 |
31 | ## Task 5 Solution
32 |
33 | - xxxxx
34 | - xxxxx
35 | - xxxxx
36 |
--------------------------------------------------------------------------------
/vscode-extensions/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Visual Studio Code - Install extensions",
3 | "description": "Install extensions",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "😺",
8 | "courseData": "setup.sh",
9 | "code": "foreground.sh",
10 | "text": "step1.md"
11 | }
12 | ]
13 | },
14 | "environment": {
15 | "hideintro": true,
16 | "uilayout": "terminal",
17 | "uieditorpath": "/root/example",
18 | "showide": true,
19 | "ide": "ssh host01 '/opt/start-vscode.sh /root/example'"
20 | },
21 | "backend": {
22 | "imageid": "kubernetes-cluster-running:1.18"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/challenges/node-challenge-1/.cypress/node_spec.js:
--------------------------------------------------------------------------------
1 | describe('Node.js Challenge', () => {
2 | before(() => {
3 | cy.startScenario()
4 | });
5 |
6 | it('starts on Task 1', () => {
7 | cy.terminalShouldContain('Task 1');
8 | cy.terminalNotShouldContain('Task 2');
9 | });
10 |
11 | it('completing Task 1 completes the challenge', () => {
12 | cy.viewport(2560, 1600)
13 |
14 | cy.terminalShouldContain('Task 1');
15 |
16 | cy.terminalNotShouldContain('Completed Challenge');
17 |
18 | cy.terminalType("cheat");
19 |
20 | cy.terminalShouldContain('✅');
21 |
22 | cy.terminalShouldContain('Completed Challenge');
23 | });
24 | })
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/assets/example-asset.txt:
--------------------------------------------------------------------------------
1 | Hi! I'm just an example asset. Don't mind me.
2 |
3 | I'm only here because the index.json specified to copy over
4 | the contents of the scenario's source /assets/ folder into
5 | the environment's /root/ folder, like so:
6 |
7 | "assets": {
8 | "client": [
9 | { "file": "*", "target": "~/" }
10 | ]
11 | }
12 |
13 | The ~/ just refers to the environment user's home folder.
14 | That user is named "root". Once the environment loads, try
15 | confirming that this example got copied over with:
16 |
17 | $ pwd
18 | /root
19 |
20 | $ ls
21 | example-asset.txt
--------------------------------------------------------------------------------
/displaying-web-ui/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Displaying Web UI ",
3 | "description": "Learn how to access a Web UI running on the environment",
4 | "difficulty": "easy",
5 | "time": "10 minutes",
6 | "details": {
7 | "steps": [
8 | {
9 | "text": "step1.md"
10 | }
11 | ],
12 | "intro": {
13 | "text": "intro.md"
14 | },
15 | "finish": {
16 | "text": "finish.md"
17 | }
18 | },
19 | "environment": {
20 | "hideintro": true,
21 | "uilayout": "terminal"
22 | },
23 | "backend": {
24 | "imageid": "ubuntu"
25 | }
26 | }
--------------------------------------------------------------------------------
/use-ip-or-dns-for-environment/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "title": "Get the IP or DNS name for the environment",
4 | "description": "Learn how to get the IP address or DNS name for configuration scripts",
5 | "difficulty": "Beginner",
6 | "time": "60 minutes",
7 | "details": {
8 | "steps": [
9 | {
10 | "text": "step1.md",
11 | "code": "foreground.sh"
12 | }
13 | ],
14 | "intro": {
15 | "text": "intro.md"
16 | },
17 | "finish": {
18 | "text": "finish.md"
19 | }
20 | },
21 | "environment": {
22 | "uilayout": "terminal"
23 | },
24 | "backend": {
25 | "imageid": "ubuntu"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal-with-vscode/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Terminal and VS Code UI as Tab",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "files": [
14 | "app.js", "index.js"
15 | ],
16 | "environment": {
17 | "uieditorpath": "/root/example",
18 | "showide": true,
19 | "idePort": 23000,
20 | "uilayout": "terminal"
21 | },
22 | "backend": {
23 | "imageid": "ubuntu:2004"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/quiz/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Interactive Quiz",
4 | "description": "Verify understanding and key points by using an interactive quiz",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Quiz",
9 | "text": "step1.md"
10 | }
11 | ],
12 | "intro": {
13 | "text": "intro.md",
14 | "credits": ""
15 | },
16 | "finish": {
17 | "text": "finish.md"
18 | }
19 | },
20 | "files": [],
21 | "environment": {
22 | "uilayout": "terminal"
23 | },
24 | "backend": {
25 | "imageid": "ubuntu"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/run-commands-automatically/step1.md:
--------------------------------------------------------------------------------
1 | Commands can be run automatically in the foreground and background when a scenario starts and when a step loads.
2 |
3 | The `index.json` file contains the scenario structure.
4 |
5 | Within the `intro` block and for each step, two files can be defined under `courseData` and `code`.
6 |
7 | `courseData` defines a script which runs in the background.
8 |
9 | `code` defines the commands to run in the foreground.
10 |
11 |
12 | "intro": {
13 | "text": "intro.md",
14 | "courseData": "background.sh",
15 | "code": "foreground.sh",
16 | "credits": ""
17 | }
18 |
19 |
20 | When the user proceeds to the next step, additional scripts can be run.
21 |
--------------------------------------------------------------------------------
/embed-videos/step1.md:
--------------------------------------------------------------------------------
1 | Within the Markdown content for Katacoda you can embed any HTML element, this means you would have the ability to embed YouTube videos, such as the example below.
2 |
3 |
4 |
5 | The HTML included is:
6 |
7 | 8 | <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/KeJJ34BvA7Q" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> 9 |10 | -------------------------------------------------------------------------------- /embed-videos/intro.md: -------------------------------------------------------------------------------- 1 | Example of embedding a video on the intro screen. 2 | 3 | 4 | 5 | Here is the HTML example: 6 | 7 |
8 | <iframe style="width: 700px;height: 400px;" src="https://www.youtube-nocookie.com/embed/KeJJ34BvA7Q" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> 9 |10 | 11 | *Note:* On the Intro panel, the width and height must be set via the style attribute. 12 | -------------------------------------------------------------------------------- /credit-where-credit-due/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Credit where credit due", 3 | "description": "Possible ways to provide credit for the content or ideas within the scenario", 4 | "difficulty": "Beginner", 5 | "time": "5 minutes", 6 | "details": { 7 | "steps": [ 8 | { 9 | "title": "Hello World", 10 | "text": "step1.md" 11 | } 12 | ], 13 | "intro": { 14 | "text": "intro.md", 15 | "credits": "Here we can provide **credit** to others such as a link to a https://github.com repo" 16 | }, 17 | "finish": { 18 | "text": "finish.md" 19 | } 20 | }, 21 | "environment": { 22 | "uilayout": "terminal" 23 | }, 24 | "backend": { 25 | "imageid": "bash" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /exclusion-patterns/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "exclusion-patterns", 3 | "title": "Exclusion patterns", 4 | "description": "Exclude files or directories of your scenario", 5 | "details": { 6 | "steps": [ 7 | { 8 | "title": "Exclusion patterns", 9 | "text": "step1.md" 10 | } 11 | ] 12 | }, 13 | "files": [ 14 | "poc.sh" 15 | ], 16 | "environment": { 17 | "hideintro": true, 18 | "hidefinish": true, 19 | "uilayout": "editor-terminal-v1", 20 | "uisettings": "bash", 21 | "exclusionPatterns": ["*test", "./test/*", "./logs"] 22 | }, 23 | "backend": { 24 | "imageid": "nodejs:12" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /notebooks-py/Index.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# A example Notebook\n", 10 | "## Katacoda" 11 | ] 12 | } 13 | ], 14 | "metadata": { 15 | "kernelspec": { 16 | "display_name": "Python 3", 17 | "language": "python", 18 | "name": "python3" 19 | }, 20 | "language_info": { 21 | "codemirror_mode": { 22 | "name": "ipython", 23 | "version": 3 24 | }, 25 | "file_extension": ".py", 26 | "mimetype": "text/x-python", 27 | "name": "python", 28 | "nbconvert_exporter": "python", 29 | "pygments_lexer": "ipython3", 30 | "version": "3.8.6" 31 | } 32 | }, 33 | "nbformat": 4, 34 | "nbformat_minor": 4 35 | } 36 | -------------------------------------------------------------------------------- /notebooks-py-slim/Index.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "# A example Notebook\n", 10 | "## Katacoda" 11 | ] 12 | } 13 | ], 14 | "metadata": { 15 | "kernelspec": { 16 | "display_name": "Python 3", 17 | "language": "python", 18 | "name": "python3" 19 | }, 20 | "language_info": { 21 | "codemirror_mode": { 22 | "name": "ipython", 23 | "version": 3 24 | }, 25 | "file_extension": ".py", 26 | "mimetype": "text/x-python", 27 | "name": "python", 28 | "nbconvert_exporter": "python", 29 | "pygments_lexer": "ipython3", 30 | "version": "3.8.6" 31 | } 32 | }, 33 | "nbformat": 4, 34 | "nbformat_minor": 4 35 | } 36 | -------------------------------------------------------------------------------- /uilayouts/uilayout-terminal-iframe/step1.md: -------------------------------------------------------------------------------- 1 | The Katacoda `terminal-iframe` UI Layout provides a full Terminal experience with the ability to include webpages as iframes alongside the content. 2 | 3 | # Index.json 4 | 5 | Example: 6 | 7 |
8 | "environment": {
9 | "showdashboards": true,
10 | "uilayout": "terminal-iframe"
11 | },
12 | "backend": {
13 | "imageid": "nodejs:12",
14 | "port": 3000
15 | }
16 |
17 |
18 | More information can be found at https://katacoda.com/scenario-examples/scenarios/dashboard-tabs-iframe
19 |
20 | # Helper Functionality
21 |
22 | View the iFrame https://[[HOST_SUBDOMAIN]]-3000-[[KATACODA_HOST]].environments.katacoda.com
23 |
24 | Other links open in a new tab https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com
--------------------------------------------------------------------------------
/placeholders/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Using Katacoda Placeholders for dynamic content",
3 | "description": "Learn how to get the IP address and URL parts for the environment",
4 | "difficulty": "Beginner",
5 | "time": "10",
6 | "details": {
7 | "steps": [
8 | {
9 | "title": "IP Addresses",
10 | "code": "foreground1.sh",
11 | "text": "step1.md"
12 | },
13 | {
14 | "title": "URLs",
15 | "code": "foreground2.sh",
16 | "text": "step2.md"
17 | }
18 | ],
19 | "intro": {
20 | "text": "intro.md"
21 | },
22 | "finish": {
23 | "text": "finish.md"
24 | }
25 | },
26 | "environment": {
27 | "uilayout": "terminal"
28 | },
29 | "backend": {
30 | "imageid": "docker-swarm"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/training/training-example-pathway.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Training Pathway Example",
3 | "description": "When using Katacoda for Trainers, the pathway defines the course for attendees",
4 | "courses": [
5 | {
6 | "pathway_id": "istio",
7 | "id": "workshop/deploying-istio",
8 | "course_id": "deploying-istio",
9 | "title": "Deploying Istio to Kubernetes",
10 | "description": "Learn how to deploy Istio to a Kubernetes cluster"
11 | },
12 | {
13 | "pathway_id": "istio",
14 | "id": "connecting-controlling-microservices/traffic-shaping",
15 | "course_id": "traffic-shaping",
16 | "title": "Connecting & Controlling: Traffic Shaping Microservices Connections",
17 | "description": "Learn Load Balancing, Routes, Rules with Istio"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-editor-terminal/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Scenario with Editor Terminal UI",
4 | "description": "Katacoda Scenario Example",
5 | "details": {
6 | "steps": [
7 | {
8 | "text": "step1.md",
9 | "code": "setup.sh"
10 | }
11 | ],
12 | "intro": {
13 | "text": "intro.md",
14 | "credits": ""
15 | }
16 | },
17 | "files": [
18 | "app.js", "index.js"
19 | ],
20 | "environment": {
21 | "hidefinish": true,
22 | "hideHiddenFiles": true,
23 | "uilayout": "editor-terminal",
24 | "uisettings": "javascript",
25 | "uieditorpath": "/root/example"
26 | },
27 | "backend": {
28 | "imageid": "nodejs:12"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/verified-steps/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Verifying user actions during scenario",
3 | "description": "Run checks to make sure the user can proceed. Scenario also offers user solutions if they struggle",
4 | "noindex": true,
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Verify",
9 | "text": "step1.md",
10 | "verify": "step1-verify.sh"
11 | },
12 | {
13 | "title": "Show Answers",
14 | "text": "step2.md",
15 | "solutionText": "step2-answer.md"
16 | }
17 | ]
18 | },
19 | "environment": {
20 | "hideintro": true,
21 | "hidefinish": true,
22 | "uilayout": "terminal"
23 | },
24 | "backend": {
25 | "imageid": "git"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/1_hint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # See hint documentation: https://www.katacoda.community/challenges.html#hints
4 |
5 | seconds_sofar=$1
6 |
7 | # echo "Debug Hint Task 1: $seconds_sofar"
8 |
9 | # This hint message will appear between 10-20 seconds elapsed after the task began
10 | if [[ $seconds_sofar -ge 10 && $seconds_sofar -lt 20 ]]; then
11 | echo "Still working on it? I'll show a hint very soon..."
12 | fi
13 |
14 | # This hint message will appear 20+ seconds elapsed after the task began
15 | if [ $seconds_sofar -ge 20 ]; then
16 | echo "**Hint:** Normally, this hint would give help nudge you toward the solution. (As it is, I'm just going to tell you the answer, in case you're new to the command line. The simplest way to create a new file is the command 'touch'. Try typing 'touch bananas.txt' and then hit enter.)"
17 | fi
18 |
--------------------------------------------------------------------------------
/visualise-docker/step1.md:
--------------------------------------------------------------------------------
1 | Katacoda has integrated [Weave Scope](https://weave.works/scope) to help visualise Docker and Kubernetes based deployments.
2 |
3 | The aim of integrating Scope is to help people understand what has been deployed and as changes new aspects are deployed to automatically see the impact.
4 |
5 | To launch Scope, click the Tab.
6 |
7 | ## Index.json
8 |
9 | To add the visualise tab to your scenarios, include the following JSON snippet within the environments node. This will automatically add the tab to the scenarios. When the tab is pressed, the command to launch the Scope container will run.
10 |
11 | 12 | "showvisualise": true, 13 | "scope": "docker run --name=scope -d --net=host --pid=host --privileged -v /var/run/docker.sock:/var/run/docker.sock:rw weaveworks/scope:1.9.1 --probe.docker=true", 14 | "scopePort": 4040, 15 |16 | -------------------------------------------------------------------------------- /vscode-extensions/step1.md: -------------------------------------------------------------------------------- 1 | To install a VSCode extension execute the following in a background script: 2 | 3 | The use of bsdtar is required due to the packaging format of VS Extensions. 4 | 5 | ```sh 6 | echo 'debconf debconf/frontend select Noninteractive' | sudo debconf-set-selections 7 | cd /tmp && apt-get update -y && apt install libarchive-tools -y # install bsdtar 8 | curl -L https://marketplace.visualstudio.com/_apis/public/gallery/publishers/humao/vsextensions/rest-client/0.24.3/vspackage | bsdtar -xvf - extension 9 | mv extension /opt/.katacodacode/extensions/humao.rest-client-0.24.3 10 | ``` 11 | 12 | The name of the directory for the extension should use the same pattern that when the extension is installed manually `
8 | #!/bin/bash 9 | 10 | echo "This is a background script with a long running process" 11 | 12 | sleep 10 13 | 14 | echo "done" >> /opt/.backgroundfinished 15 | 16 |17 | 18 | The foreground script waits for the file to be created. After the file exists, the while loop finishes and the user is allowed to access the terminal. 19 | 20 |
21 | echo "Waiting to complete"; while [ ! -f /opt/.backgroundfinished ] ; do sleep 2; done; echo "Done" 22 |23 | 24 | This can be improved by displaying a Progress Spinner to the user as described in an additional scenario. 25 | -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/task_3.md: -------------------------------------------------------------------------------- 1 | There is a famous play and 1951 film _A Streetcar Named Desire_ by Tennessee Williams. It has a famous line, "Hey Stella!". Stella Kowalski is one of the main characters in the play. [Marlon Brando said her name the best](https://www.youtube.com/watch?v=S1A0p0F_iH8). Search the play's manuscript to see how many times her name is spoken with and without a passionate exclamation point. Create a property file called `stella.properties` and place the word counts for each of these keys: 2 | 3 | ```properties 4 | stella=? 5 | stella_exclamation=? 6 | ``` 7 | 8 | In the current directory, there is a file `streetcar.pdf`. Using [pdftotext](https://ubuntu.pkgs.org/20.04/ubuntu-main-arm64/poppler-utils_0.86.1-0ubuntu1_arm64.deb.html), you'll need to convert this `streetcar.pdf` to `streetcar.txt` to count the words. 9 | 10 | 💡 Consider that water closets are no places to count words. 11 | -------------------------------------------------------------------------------- /course-data-host01-reference/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Background script with explicit reference to host01", 3 | "difficulty": "beginner", 4 | "time": "10 minutes", 5 | "details": { 6 | "assets": { 7 | "host01": [ 8 | { 9 | "file": "wait-script.sh", 10 | "target": "/tmp/" 11 | }, 12 | { 13 | "file": "dummy.json", 14 | "target": "/root" 15 | } 16 | ] 17 | }, 18 | "steps": [ 19 | { 20 | "title": "Commands", 21 | "text": "step1.md" 22 | } 23 | ], 24 | "intro": { 25 | "text": "intro.md", 26 | "code": "set-env.sh", 27 | "courseData": "init-env.sh" 28 | } 29 | }, 30 | "environment": { 31 | "uilayout": "terminal", 32 | "hidefinish": true, 33 | "hideintro": true 34 | }, 35 | "backend": { 36 | "imageid": "kubernetes-cluster-running:1.18" 37 | } 38 | } -------------------------------------------------------------------------------- /challenges/kubernetes-challenge-1/.cypress/k8s_spec.js: -------------------------------------------------------------------------------- 1 | describe('Kubernetes Challenge', () => { 2 | before(() => { 3 | cy.startScenario() 4 | }); 5 | 6 | it('starts on Task 1', () => { 7 | cy.terminalShouldContain('Task 1'); 8 | cy.terminalNotShouldContain('Task 2'); 9 | }); 10 | 11 | it('can complete challenge 1', () => { 12 | cy.viewport(2560, 1600) 13 | 14 | cy.terminalShouldContain('Task 1'); 15 | 16 | cy.terminalNotShouldContain('Task 2'); 17 | 18 | cy.terminalType('curl "node01:30080?cmd=env" > /root/app-env'); 19 | 20 | cy.terminalShouldContain('Task 2'); 21 | 22 | }); 23 | 24 | it('can complete challenge 2', () => { 25 | cy.viewport(2560, 1600) 26 | 27 | cy.terminalShouldContain('Task 2'); 28 | 29 | cy.terminalType('curl "node01:30080?cmd=cat%20app.js" > /root/app.js'); 30 | 31 | cy.terminalShouldContain('Completed Challenge'); 32 | 33 | }); 34 | }) -------------------------------------------------------------------------------- /challenges/node-challenge-1/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "challenge@0.8", 3 | "title": "Challenge Example - Node.js", 4 | "description": "", 5 | "difficulty": "Beginner", 6 | "time": "60 minutes", 7 | "details": { 8 | "steps": [ 9 | { 10 | "title": "Fix Tests", 11 | "text": "1_task.md", 12 | "verify": "1_verify.sh" 13 | } 14 | ], 15 | "intro": { 16 | "text": "intro.md", 17 | "background": "background.sh" 18 | }, 19 | "finish": { 20 | "text": "finish.md" 21 | }, 22 | "assets": { 23 | "client": [ 24 | { 25 | "file": "cheat", 26 | "target": "/usr/local/bin/", 27 | "chmod": "+x" 28 | } 29 | ] 30 | } 31 | }, 32 | "environment": { 33 | "hidesidebar": true, 34 | "uilayout": "vscode-terminal-split" 35 | }, 36 | "backend": { 37 | "imageid": "nodejs:12" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /create-course/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Creating a Katacoda Course", 3 | "description": "Learn how to create a collection of Katacoda scenarios into a course", 4 | "details": { 5 | "steps": [ 6 | { 7 | "title": "Pathway Files", 8 | "text": "step1.md" 9 | }, 10 | { 11 | "title": "Reusing Scenarios", 12 | "text": "step2.md" 13 | }, 14 | { 15 | "title": "Using katacoda CLI to create a course", 16 | "text": "step3.md" 17 | }, 18 | { 19 | "title": "Using katacoda CLI to add existing scenarios to a course", 20 | "text": "step4.md" 21 | } 22 | ], 23 | "intro": { 24 | "text": "intro.md" 25 | }, 26 | "finish": { 27 | "text": "finish.md" 28 | } 29 | }, 30 | "environment": { 31 | "uilayout": "editor-terminal" 32 | }, 33 | "backend": { 34 | "imageid": "nodejs:12" 35 | } 36 | } -------------------------------------------------------------------------------- /verified-steps/step1.md: -------------------------------------------------------------------------------- 1 | Ensuring that users have successfully completed certain actions before the proceed can be a useful technique to ensure that are paying attention and they won't hit problems later on in the scenario. 2 | 3 | When the user clicks Continue, a script is run against the environment. 4 | 5 | For the user to be allowed to proceed, the script should output the text "done". For example, the following verify script checks to make sure the user has run `git init`{{execute}} before they can proceed. 6 | 7 |
8 | [ -d /home/scrapbook/tutorial/.git ] && echo "done" 9 |10 | 11 | If you run `git init`{{execute}}, you will be allowed to continue. 12 | 13 | The `index.json` example is: 14 |
15 | "details": {
16 | "steps": [
17 | {
18 | "title": "Verify",
19 | "text": "step1.md",
20 | "verify": "step1-verify.sh"
21 | }
22 | ]
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/dashboard-tabs-iframe/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Dashboard and Terminal Tabs (iframe)",
4 | "description": "Add Additional Tabs to the UI with iframe integration",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Tab Types",
9 | "text": "step1.md"
10 | }
11 | ]
12 | },
13 | "environment": {
14 | "showide": true,
15 | "hideintro": true,
16 | "showdashboard": true,
17 | "dashboards": [
18 | {"name": "URL", "href": "https://www.katacoda.com"},
19 | {"name": "Port 80", "port": 80},
20 | {"name": "Placeholder", "href": "https://[[HOST_SUBDOMAIN]]-8080-[[KATACODA_HOST]].environments.katacoda.com/somepage", "port": 8080}],
21 | "uilayout": "terminal-iframe"
22 | },
23 | "backend": {
24 | "imageid": "ubuntu",
25 | "port": 80
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/challenges-pathway.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Challenges Examples",
3 | "description": "Examples of how to create Challenges on Katacoda",
4 | "courses": [{
5 | "course_id": "challenge-hello-world",
6 | "title": "Challenge Template",
7 | "description": "Basic template for a challenge"
8 | },
9 | {
10 | "course_id": "node-challenge-1",
11 | "title": "Challenge Example - Node.js",
12 | "description": "How to create a challenge on using Node.js"
13 | },
14 | {
15 | "course_id": "kubernetes-challenge-1",
16 | "title": "Challenge Example - Kubernetes",
17 | "description": "How to create a challenge on using Kubernetes"
18 | },
19 | {
20 | "course_id": "challenge-linux-solver",
21 | "title": "Challenge Example - Linux Challenge Using the Solver Utility",
22 | "description": "An example challenge showing authors how to use the solver features"
23 | }
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/create-scenario-101/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Creating Your First Katacoda Scenario",
3 | "description": "Learn how to create your first Katacoda scenario",
4 | "details": {
5 | "steps": [
6 | {
7 | "title": "Scenario Structure",
8 | "text": "step1.md"
9 | },
10 | {
11 | "title": "Scenario Syntax",
12 | "text": "step2.md"
13 | },
14 | {
15 | "title": "CLI",
16 | "text": "step3.md"
17 | },
18 | {
19 | "title": "Visual Studio Code Extension",
20 | "text": "step4.md"
21 | },
22 | {
23 | "title": "Webhooks",
24 | "text": "step5.md"
25 | }
26 | ],
27 | "intro": {
28 | "text": "intro.md"
29 | },
30 | "finish": {
31 | "text": "finish.md"
32 | }
33 | },
34 | "environment": {
35 | "uilayout": "editor-terminal"
36 | },
37 | "backend": {
38 | "imageid": "nodejs:12"
39 | }
40 | }
--------------------------------------------------------------------------------
/upload-assets/step1.md:
--------------------------------------------------------------------------------
1 | Katacoda supports automatically uploading assets to scenarios. This is designed for configuration and short scripts.
2 |
3 | The options are:
4 |
5 | 1) `file`: The name of the file within the Assets directory.
6 |
7 | 2) `target`: The directory where the file should be uploaded to.
8 |
9 | 3) `chmod`: Optional, automatically set a chmod flag after uploading the file.
10 |
11 | The following is an example from the `index.json`.
12 |
13 |
14 | "details": {
15 | "assets": {
16 | "host01": [
17 | {"file": "wait.sh", "target": "/usr/local/bin/", "chmod": "+x"},
18 | {"file": "deploy.sh", "target": "/usr/local/bin/", "chmod": "+x"}
19 | ]
20 | }
21 | },
22 |
23 |
24 | View the uploaded files:
25 |
26 | `cat /usr/local/bin/wait.sh`{{execute}}
27 |
28 | `cat /usr/local/bin/deploy.sh`{{execute}}
29 |
30 | **Note:** There is currently a file limit of 1mb. If the file is greater than 1mb, for performance, we recommend using a CDN and using cURL to download the file.
31 |
--------------------------------------------------------------------------------
/custom-events/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "custom-events",
3 | "title": "Raise Custom Events From Environments",
4 | "description": "Raise events from the user environment to Katacoda",
5 | "details": {
6 | "intro": {
7 | "text": "intro.md",
8 | "code": "env-init.sh",
9 | "credits": ""
10 | },
11 | "steps": [
12 | {
13 | "title": "Custom events",
14 | "text": "step1.md"
15 | }
16 | ]
17 | },
18 | "asyncFiles": ["index.html"],
19 | "environment": {
20 | "hidefinish": true,
21 | "showdashboard": true,
22 | "delayToSaveFileAfterStopTypingMilliseconds": 2000,
23 | "dashboards": [
24 | {"name": "Web Application", "port": 3000}
25 | ],
26 | "uilayout": "editor-iframe-split",
27 | "uisettings": "html"
28 | },
29 | "backend": {
30 | "imageid": "nodejs:12",
31 | "port": 3000
32 | },
33 | "events": {
34 | "hideprogressbar": "reload-finished"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/clipboard/step1.md:
--------------------------------------------------------------------------------
1 | This is an example of Katacoda's clipboard integration.
2 |
3 | The following will copy the text into the clipboard. This is useful if users need to interact with a web interface.
4 |
5 | `docker`{{copy}}
6 |
7 | Within the Markdown step, you would write:
8 |
9 | `docker`{{copy}}
10 |
11 |
12 | Katacoda supports copying code snippets or longer text into the clipboard by adding the attribute `data-target`.
13 |
14 | 15 | Copy Me To The Clipboard!! 16 |17 | 18 | This is created by embedded HTML into the Markdown. 19 | 20 |
21 | <pre class="file" data-target="clipboard"> 22 | Copy Me To The Clipboard!! 23 | </pre> 24 |25 | 26 | **Note** Without the class="file" it will not display the clipboard functionality. For example: 27 | 28 |
29 | Not a file 30 |31 | 32 | This was created by the HTML: 33 | 34 |
35 | <pre data-target="clipboard"> 36 | Not a file 37 | </pre> 38 |39 | -------------------------------------------------------------------------------- /upload-assets/assets/wait.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | show_progress() 4 | { 5 | echo -n "Starting" 6 | local -r pid="${1}" 7 | local -r delay='0.75' 8 | local spinstr='\|/-' 9 | local temp 10 | while true; do 11 | sudo grep -i "done" /root/katacoda-finished &> /dev/null 12 | if [[ "$?" -ne 0 ]]; then 13 | temp="${spinstr#?}" 14 | printf " [%c] " "${spinstr}" 15 | spinstr=${temp}${spinstr%"${temp}"} 16 | sleep "${delay}" 17 | printf "\b\b\b\b\b\b" 18 | else 19 | break 20 | fi 21 | done 22 | printf " \b\b\b\b" 23 | echo "" 24 | echo "Started" 25 | echo -n "Configuring" 26 | while true; do 27 | sudo grep -i "done" /root/katacoda-background-finished &> /dev/null 28 | if [[ "$?" -ne 0 ]]; then 29 | temp="${spinstr#?}" 30 | printf " [%c] " "${spinstr}" 31 | spinstr=${temp}${spinstr%"${temp}"} 32 | sleep "${delay}" 33 | printf "\b\b\b\b\b\b" 34 | else 35 | break 36 | fi 37 | done 38 | printf " \b\b\b\b" 39 | echo "" 40 | echo "Configured" 41 | } 42 | 43 | show_progress -------------------------------------------------------------------------------- /displaying-progress-spinner/assets/wait.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | show_progress() 4 | { 5 | echo -n "Starting" 6 | local -r pid="${1}" 7 | local -r delay='0.75' 8 | local spinstr='\|/-' 9 | local temp 10 | while true; do 11 | sudo grep -i "done" /root/katacoda-finished &> /dev/null 12 | if [[ "$?" -ne 0 ]]; then 13 | temp="${spinstr#?}" 14 | printf " [%c] " "${spinstr}" 15 | spinstr=${temp}${spinstr%"${temp}"} 16 | sleep "${delay}" 17 | printf "\b\b\b\b\b\b" 18 | else 19 | break 20 | fi 21 | done 22 | printf " \b\b\b\b" 23 | echo "" 24 | echo "Started" 25 | echo -n "Configuring" 26 | while true; do 27 | sudo grep -i "done" /root/katacoda-background-finished &> /dev/null 28 | if [[ "$?" -ne 0 ]]; then 29 | temp="${spinstr#?}" 30 | printf " [%c] " "${spinstr}" 31 | spinstr=${temp}${spinstr%"${temp}"} 32 | sleep "${delay}" 33 | printf "\b\b\b\b\b\b" 34 | else 35 | break 36 | fi 37 | done 38 | printf " \b\b\b\b" 39 | echo "" 40 | echo "Configured" 41 | } 42 | 43 | show_progress -------------------------------------------------------------------------------- /create-course/step4.md: -------------------------------------------------------------------------------- 1 | The CLI can be used to add existing scenarios to a course. 2 | 3 | You can list your available scenarios, using this command: 4 | 5 | `find ./ -type f | grep index.json`{{execute}} 6 | 7 | For example, can add the existing scenario, called *quiz* to the previous created course *test-course* running the following command and follow the steps: 8 | 9 | `katacoda courses:add:scenarios`{{execute}} 10 | 11 | The CLI will prompt you a few questions about: 12 | 13 | - Scenario path: type `./quiz` 14 | - Course path: type `./test-course` 15 | 16 | And then select if you want to copy or move the scenario to the course folder. 17 | 18 | After that, you can see the scenario was added in your `test-course` opening `katacoda-scenario-examples/test-course-pathway.json`{{open}} 19 | 20 |
21 | {
22 | "title": "test-course",
23 | "description": "Example course",
24 | "courses": [
25 | {
26 | "course_id": "quiz",
27 | "title": "Interactive Quiz",
28 | "description": "Verify understand and key points by using an interactive quiz"
29 | }
30 | ]
31 |
32 |
--------------------------------------------------------------------------------
/dashboard-tabs/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "noindex": true,
3 | "title": "Dashboard and Terminal Tabs",
4 | "description": "Add Additional Tabs to the UI",
5 | "details": {
6 | "steps": [
7 | {
8 | "title": "Dashboard Tabs",
9 | "text": "step1.md"
10 | },
11 | {
12 | "title": "Terminal Tabs",
13 | "text": "step2.md"
14 | }
15 | ]
16 | },
17 | "environment": {
18 | "hideintro": true,
19 | "showdashboard": true,
20 | "dashboards": [{"name": "Port 80", "port": 80},
21 | {"name": "URL", "href": "https://www.google.co.uk"},
22 | {"name": "Placeholder", "href": "https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com"}],
23 | "terminals": [
24 | {"name": "Terminal 2", "target": "host01"},
25 | {"name": "Docker Stats", "command": "docker stats", "target": "host01"}
26 | ],
27 | "uilayout": "terminal"
28 | },
29 | "backend": {
30 | "imageid": "ubuntu",
31 | "port": 80
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/dashboard-tabs-iframe/step1.md:
--------------------------------------------------------------------------------
1 | By default the Dashboard Tabs will open in a new window. When combined with the layout `terminal-iframe`, the Dashboard tabs can be embedded as an iframe.
2 |
3 | This allows the user to keep a consistent view and allow the steps to explain actions that need to be performed within the UI.
4 |
5 |
6 | "environment": {
7 | "showdashboard": true,
8 | "dashboards": [{"name": "URL", "href": "https://www.katacoda.com"},
9 | {"name": "Port 80", "port": 80},
10 | {"name": "Placeholder", "href": "https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com"}],
11 | "uilayout": "terminal-iframe"
12 | }
13 |
14 |
15 | ## Switching User Context
16 |
17 | If the URL matches the iframe then links will automatically switch the user to the correct tab when they are selected, such as https://www.katacoda.com and https://[[HOST_SUBDOMAIN]]-8080-[[KATACODA_HOST]].environments.katacoda.com
18 |
19 | **Note:** This approach uses iframes, as such, websites such as Google.co.uk will not load due to Browser Security and Google's iframe configuration. Look in your browser's console to view the errors.
20 |
--------------------------------------------------------------------------------
/challenges/kubernetes-challenge-1/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "challenge@0.8",
3 | "title": "Challenge Example - Kubernetes",
4 | "description": "",
5 | "difficulty": "Advance",
6 | "time": "60 minutes",
7 | "details": {
8 | "steps": [
9 | {
10 | "title": "Output env",
11 | "text": "1_task.md",
12 | "verify": "1_verify.sh",
13 | "hint": "1_hint.sh"
14 | },
15 | {
16 | "title": "Extract app.js",
17 | "text": "2_task.md",
18 | "verify": "2_verify.sh"
19 | }
20 | ],
21 | "intro": {
22 | "text": "intro.md",
23 | "code": "foreground.sh",
24 | "courseData": "configure.sh"
25 | },
26 | "assets": {
27 | "host01": [
28 | {"file": "cheat", "target": "/usr/local/bin/", "chmod": "+x"},
29 | {"file": "cheat2", "target": "/usr/local/bin/", "chmod": "+x"},
30 | {"file": "deploy.yaml", "target": "/opt/"}
31 | ]
32 | }
33 | },
34 | "environment": {
35 | "uilayout": "terminal",
36 | "showide": true,
37 | "hidesidebar": true
38 | },
39 | "backend": {
40 | "imageid": "kubernetes-cluster-running:1.18"
41 | }
42 | }
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "challenge@0.8",
3 | "title": "Challenge Template",
4 | "description": "Basic template for a challenge",
5 | "difficulty": "Beginner",
6 | "time": "5 minutes",
7 | "details": {
8 | "steps": [
9 | {
10 | "title": "Bananas",
11 | "text": "1_task.md",
12 | "verify": "1_verify.sh",
13 | "hint": "1_hint.sh"
14 | },
15 | {
16 | "title": "Apples",
17 | "text": "2_task.md",
18 | "verify": "2_verify.sh",
19 | "hint": "2_hint.sh"
20 | }
21 | ],
22 | "intro": {
23 | "text": "intro.md"
24 | },
25 | "finish": {
26 | "text": "finish.md"
27 | },
28 | "assets": {
29 | "client": [
30 | { "file": "*", "target": "~/" }
31 | ]
32 | }
33 | },
34 | "environment": {
35 | "uilayout": "editor-terminal",
36 | "hideHiddenFiles": true
37 | },
38 | "backend": {
39 | "imageid": "ubuntu:2004"
40 | }
41 | }
--------------------------------------------------------------------------------
/exclusion-patterns/step1.md:
--------------------------------------------------------------------------------
1 | Let's create a script that adds a new test folder and creates a file every 1 second. Also this script register the message in a log file.
2 |
3 | 4 | rm -rf test/ 5 | mkdir test/ 6 | for i in $(seq 1 50000); 7 | do 8 | echo "Welcome $i times" >> logs 9 | touch test/$i.cache 10 | sleep 1 11 | done 12 |13 | 14 | Add execution permissions to the script 15 | `chmod +x ./poc.sh`{{execute}} 16 | 17 | Execute the script 18 | `./poc.sh`{{execute}} 19 | 20 | If we see, the numbers of files in the test directory are increasing every 1 second, but, in the editor, we don't see those files. 21 | `watch ls -la ./test`{{execute T2}} 22 | 23 | This is accomplished using the property `exclusionPatterns` in the definition of the scenario `index.json`. 24 | 25 |
26 | "environment": {
27 | ...
28 | "exclusionPatterns": ["*test", "./test/*", "./logs"]
29 | }
30 |
31 |
32 | Also, we can exclude the hidden files, setting the property `hideHiddenFiles` in true
33 |
34 |
35 | "environment": {
36 | ...
37 | "hideHiddenFiles": true,
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/create-course/step1.md:
--------------------------------------------------------------------------------
1 | A Katacoda course is defined by creating a pathway file. The pathway defines which scenarios should be included in the course and the order to display them.
2 |
3 | The collection of examples contains two courses, one to describe all the Katacoda content and another to describe the key Katacoda environments.
4 |
5 | ## Clone Example
6 |
7 | Clone the example repository with the command `git clone https://github.com/katacoda/scenario-examples.git katacoda-scenario-examples`{{execute}}
8 |
9 | Within the root of a repository, a course has been created called `uilayouts`. The contents of the course have been defined as `katacoda-scenario-examples/uilayouts-pathway.json`{{open}}.
10 |
11 | Within the JSON file, the courses element defines each scenario. For example:
12 |
13 |
14 | {
15 | "course_id": "uilayout-terminal",
16 | "title": "Scenario with Terminal UI",
17 | "description": "Katacoda Scenario Example"
18 | },
19 |
20 |
21 | The **course_id** is the scenario name directory within the course directory. For example `ls katacoda-scenario-examples/uilayouts/uilayout-terminal`{{execute}}. The **title** and **description** are shown on the course page.
22 |
--------------------------------------------------------------------------------
/automatedtest/.cypress/example_spec.js:
--------------------------------------------------------------------------------
1 | describe('My First Test', () => {
2 | before(() => {
3 | cy.startScenario() // This would automatically go to the scenario being tested. Without this the test will fail.
4 | });
5 |
6 | it('finds the "Start Scenario" button', () => {
7 | cy.contains('Start Scenario');
8 | });
9 |
10 | it('can run a command', () => {
11 | cy.terminalType("uptime");
12 |
13 | cy.terminalShouldContain('load average');
14 | });
15 |
16 | it('can verify contents of a step', () => {
17 | cy.stepShouldBeVisible(1);
18 |
19 | cy.stepHasText("This is an example scenario that will be tested using the Cypress integration");
20 | });
21 |
22 | it('can go to the next step and verify contents', () => {
23 | cy.goToStep(2);
24 |
25 | cy.stepShouldBeVisible(2);
26 |
27 | cy.stepHasText('This will be the second step.');
28 | });
29 |
30 | it('can run execute snippets based on name', () => {
31 | // TODO: The goToStep should be able to ignore being asked to move to the same step. At the moment this test depends on the above.
32 | // cy.goToStep(2);
33 |
34 | cy.get('code.execute.test-uname').click();
35 |
36 | cy.terminalShouldContain('Linux');
37 | });
38 | })
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/assets/solutions.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This script is called by the solver script. Each function should be named solve_task_#,
4 | # where # is the number of the task each function will solve. The calling solver will call
5 | # the corresponding verify_task_# function after each solver_task_# function completes.
6 |
7 | function solve_task_1() {
8 | yes | sudo apt update
9 | yes | sudo apt-get install cowsay
10 | cowsay "O'Reilly, inspiring the future for more than 40 years" > cowsay.txt
11 | }
12 |
13 |
14 | function solve_task_2() {
15 | cat /etc/passwd | column -t -s : > kinder-passwd.txt
16 | }
17 |
18 |
19 | function solve_task_3() {
20 | yes | sudo apt update
21 | yes | sudo apt-get install poppler-utils
22 | pdftotext streetcar.pdf streetcar.txt
23 | echo "stella=$(grep -c "Stella" streetcar.txt)" > stella.properties
24 | echo "stella_exclamation=$(grep -c "Stella!" streetcar.txt)" >> stella.properties
25 | }
26 |
27 |
28 | function solve_task_4() {
29 | echo -n "1 2 3 4 5" | xargs -d ' ' -i rm {}
30 |
31 | cat <
14 | "environment": {
15 | "showdashboard": true,
16 | "dashboards": [{"name": "URL", "href": "https://www.google.co.uk"},
17 | {"name": "Port 80", "port": 80},
18 | {"name": "Placeholder", "href": "https://[[HOST_SUBDOMAIN]]-80-[[KATACODA_HOST]].environments.katacoda.com"}]
19 | }
20 |
21 |
22 | The next step describes how to create additional Terminal tabs.
23 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/intro.md:
--------------------------------------------------------------------------------
1 | This challenge helps strengthen your understanding of the some basics of Linux commands. It assumes you are comfortable with basic Linux commands and know how to access the common help and documentation sources.
2 |
3 | In this challenge, you will use a basic Linux shell to complete the solutions for each task.
4 |
5 | ## Challenge
6 |
7 | In the following steps you will be asked to:
8 |
9 | ☐ Install a Linux command line tool
10 | ☐ Format some output
11 | ☐ Counts words in a PDF
12 | ☐ Show your mastery of xargs
13 |
14 |
15 | If you are unfamiliar with these concepts, [you may want to start with this documentation first](https://learning.oreilly.com/library/view/learning-modern-linux/9781098108939/).
16 |
17 | ---
18 |
19 | Your mission in this challenge, should you choose to accept it, is to complete the stated goals. This challenge presents a series of tasks as you successfully complete each goal. The scenario continuously inspects the state of your work to verify your solutions for each task. When the verification passes, you move forward. When the verification fails, be sure you understand the details of the step's goal(s) and try again. If you are really stuck, try revealing a hint.
20 |
21 | This challenge will self-destruct in 60 minutes. 🍀 **Good luck!** 🍀
22 |
--------------------------------------------------------------------------------
/dashboard-tabs/step2.md:
--------------------------------------------------------------------------------
1 | Certain scenarios will require multiple terminal tabs to be running. These can be opened dynamically using the [Markdown Extensions](), but they can also be opened when the scenario starts.
2 |
3 | ## Terminal Tab
4 |
5 | The `index.json` supports the following syntax that allows you to define an additional terminal tab.
6 |
7 |
8 | "environment": {
9 | "terminals": [{"name": "Terminal 2", "target": "host01"}]
10 | }
11 |
12 |
13 | ## Automatically Run Commands
14 |
15 | These tabs can automatically run certain commands to help the user understand what is happening. The command below will open a new Terminal tab and automatically run `docker container stats` command.
16 |
17 |
18 | "environment": {
19 | "terminals": [{"name": "Docker Stats", "command": "docker container stats", "target": "host01"}]
20 | }
21 |
22 |
23 | The can be used for tasks such as watching an event stream, or viewing all the processes running on a system. The command can also run a shell script that perhaps you create and load as an asset. Define the command as a script when a terminal starts:
24 |
25 |
26 | "environment": {
27 | "terminals": [{"name": "Docker Stats", "command": "scenario-setup-configuration", "target": "host01"}]
28 | }
29 |
30 |
31 | Ensure the script is available in the path:
32 |
33 |
34 | "assets": {
35 | "host01": [
36 | {"file": "scenario-setup-configuration.sh", "target": "/usr/local/bin", "chmod": "+x"}
37 | ]
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/create-course/step3.md:
--------------------------------------------------------------------------------
1 | Katacoda has a command line interface that helps to create the required files of courses and scenarios, etc.
2 |
3 | You can install the CLI using the following command:
4 |
5 | `npm i katacoda-cli --global`{{execute}}
6 |
7 | The commands follow the syntax:
8 |
9 | `$ katacoda COMMAND`
10 |
11 | If you need help, to see the available commands, you can run:
12 |
13 | `katacoda --help`{{execute}}
14 |
15 | To create a course, for example, called *test-course*, you can run the following command and follow the steps:
16 |
17 | ```sh
18 | cd katacoda-scenario-examples
19 | katacoda courses:create
20 | ```{{execute}}
21 |
22 | The CLI will prompt you a few questions about:
23 | - **Title:** title of the course, displayed on the intro screen
24 | - **Description:** description of the course, displayed on the intro screen
25 | - **Friendly-url:** here you will type `test-course`. This attribute will determine the name of the folder of your course, and the URL to access it, so, should not contain spaces, should be lower case, etc. For example, if your username is *test-username* and your course was called *test-course* the URL to point the course in the platform will be https://katacoda.com/test-username/courses/test-course/
26 |
27 | After that the CLI will create the pathway file and the folder.
28 | `ls test-course*`{{execute}}
29 |
30 | You can see the definition of the empty course in `katacoda-scenario-examples/test-course-pathway.json`{{open}}
31 |
32 |
33 | {
34 | "title": "test-course",
35 | "description": "Example course",
36 | "courses": []
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/delivering-training/creating-training-pathway-json/step1.md:
--------------------------------------------------------------------------------
1 | A training pathway follows the same structure as course pathway files that was discussed in the scenario [katacoda.com/scenario-examples/scenarios/create-course](https://katacoda.com/scenario-examples/scenarios/create-course).
2 |
3 | Clone the example repository with the command `git clone https://github.com/katacoda/scenario-examples.git katacoda-scenario-examples`{{execute}}
4 |
5 | Within the root of a repository the training pathway files need to be in a directory called training. The contents of the course has been defined as `katacoda-scenario-examples/training/training-example-pathway.json`{{open}}.
6 |
7 | Within the file, the training outlines the content that will be delivered. In this case, the scenario from the Istio workshop will be included.
8 |
9 |
10 | {
11 | "pathway_id": "istio",
12 | "id": "workshop/deploying-istio",
13 | "course_id": "deploying-istio",
14 | "title": "Deploying Istio to Kubernetes",
15 | "description": "Learn how to deploy Istio to a Kubernetes cluster"
16 | }
17 |
18 |
19 | The **id** is the scenario id and any courses that it could be within, in this case, the course is called workshop. The **course_id** allows the ID to be overridden and the possibility to change the URL.
20 |
21 | When live, the training would appear under the profile at [katacoda.com/scenario-examples/training/training-example](https://katacoda.com/scenario-examples/training/training-example).
22 |
23 | When delivering the training, Katacoda for Trainers, offers a number of capabilities. Firstly, attendees would have guaranteed capacity, with the possibility of increased performance that last for the entire workshop.
24 |
--------------------------------------------------------------------------------
/quiz/step1.md:
--------------------------------------------------------------------------------
1 | Katacoda supports inline interactive quiz elements that can be included as any step within the scenario. The Quiz format supports different question and answer styles. Users cannot proceed until they have answered all the questions correctly.
2 |
3 | ## Example Quiz
4 |
5 | Within Q1, users are required to enter the exact string.
6 |
7 | >>Q1: Enter the exact string test<<
8 | === test
9 |
10 | For Q2, the question requires user to enter a string containing a certain keyword.
11 |
12 | >>Q2: Enter the string containing test<<
13 | =~= test
14 |
15 | Q3 is multiple choice question, requiring users to select all the correct answers.
16 |
17 | >>Q3: Multiple Choice <<
18 | [*] Correct
19 | [*] Correct
20 | [ ] Incorrect
21 |
22 | Q4 is a single choice where users must select the correct answer.
23 |
24 | >>Q4: Single Choice <<
25 | (*) Correct
26 | ( ) Incorrect
27 |
28 | ## Markdown
29 |
30 | All of the above questions have been defined within markdown. There is no official syntax for a Quiz within markdown, as such the syntax for the above quiz is as follows:
31 |
32 | 33 | >>Q1: Enter the extract string test<< 34 | === test 35 | 36 | >>Q2: Enter the string containing test<< 37 | =~= test 38 | 39 | >>Q3: Multiple Choice << 40 | [*] Correct 41 | [*] Correct 42 | [ ] Incorrect 43 | 44 | >>Q4: Single Choice << 45 | (*) Correct 46 | ( ) Incorrect 47 |48 | 49 | The ***** within single and multiple choice indicates the correct answer. The syntax **===** is exact match, while **=~=** is a string containing match. 50 | 51 | **Note:** There should not be a blank line between the question and the possible answers. 52 | 53 | ## Correct and Incorrect Answers 54 | 55 | When a user clicks **Check Answers**, the correct answers will appear with a Green tick! If they have entered anything incorrect they will be asked to check and try again. 56 | -------------------------------------------------------------------------------- /challenges/challenge-linux-solver/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "challenge@0.8", 3 | "title": "Challenge Example - Linux Challenge Using the Solver Utility", 4 | "description": "An example challenge showing authors how to use the solver features.", 5 | "difficulty": "Beginner", 6 | "time": "15 minutes", 7 | "details": { 8 | "intro": { 9 | "text": "intro.md", 10 | "courseData": "init-background.sh", 11 | "code": "init-foreground.sh", 12 | "credits": "[Jonathan Johnson](http://www.dijure.com)" 13 | }, 14 | "finish": { 15 | "text": "finish.md" 16 | }, 17 | "steps": [ 18 | { 19 | "title": "Moo!", 20 | "text": "task_1.md", 21 | "verify": "verify.sh", 22 | "hint": "hint.sh" 23 | }, 24 | { 25 | "title": "Formatting Output", 26 | "text": "task_2.md", 27 | "verify": "verify.sh", 28 | "hint": "hint.sh" 29 | }, 30 | { 31 | "title": "Hey Stella!", 32 | "text": "task_3.md", 33 | "verify": "verify.sh", 34 | "hint": "hint.sh" 35 | }, 36 | { 37 | "title": "Powerful xargs", 38 | "text": "task_4.md", 39 | "verify": "verify.sh", 40 | "hint": "hint.sh" 41 | } 42 | ], 43 | "assets": { 44 | "host01": [ 45 | {"file": "verifications.sh", "target": "/usr/local/bin/", "chmod": "+x"}, 46 | {"file": "hints.md", "target": "/opt"}, 47 | {"file": "solutions.sh.enc", "target": "/opt"}, 48 | 49 | {"file": "streetcar.pdf", "target": "/root"}, 50 | {"file": "my-xargs.sh", "target": "/root", "chmod": "+x"} 51 | ] 52 | } 53 | }, 54 | 55 | "environment": { 56 | "showide": true, 57 | "hidesidebar": true, 58 | "uilayout": "terminal" 59 | }, 60 | 61 | "backend": { 62 | "imageid": "ubuntu:2004" 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /environments/kubernetes-cluster-1-16/master/build/1_init.sh: -------------------------------------------------------------------------------- 1 | # Build time 2 | install_kompose() { 3 | # Linux 4 | curl -L https://github.com/kubernetes/kompose/releases/download/v1.20.0/kompose-linux-amd64 -o kompose 5 | chmod +x kompose 6 | sudo mv ./kompose /usr/local/bin/kompose 7 | } 8 | 9 | install_helm() { 10 | curl -s https://storage.googleapis.com/kubernetes-helm/helm-v2.9.0-linux-amd64.tar.gz | tar xz 11 | cp linux-amd64/helm /usr/local/bin/ 12 | } 13 | 14 | ## Install CSI host path driver 15 | install_csi_driver() { 16 | git clone https://github.com/kubernetes-csi/csi-driver-host-path.git /opt/csi-driver-host-path 17 | } 18 | 19 | install_kompose 20 | install_helm 21 | install_csi_driver 22 | 23 | # Runtime 24 | cat <
10 | "environment": {
11 | ...
12 | "delayToSaveFileAfterStopTypingMilliseconds": 2000,
13 | }
14 |
15 |
16 | When a file is saved, a progress bar is shown, and the progress bar is hidden when the file was successfully saved. But for our example, we need to stop showing the progress bar when the gulp task `reload` finished.
17 |
18 | To do that, we need to need to send a message to the EventService, indicating the event we need to raise. The list of all events supported are available in this [link](https://katacoda.com/docs/scenarios/custom-events)
19 |
20 | In this example, after the `reload` task, we call to the EventService to send the `reload-finished` message. See `gulpfile.js`{{open}}, function callEventService
21 |
22 | ## Request to Event Service
23 |
24 | The request to the EventService should follow this structure:
25 |
26 | `curl -X POST https://environment-events.katacoda.com -d "socket=$1 message=message"`
27 |
28 | The socket parameter can be retrieved from the file `/tmp/socket`.
29 |
30 | ## Mapping event in index.json
31 |
32 | In the file `index.json` the message received in the EventService should be mapped to which event we want to raise, for example:
33 |
34 |
35 | "events": {
36 | "hideprogressbar": "reload-finished"
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-terminal-with-vscode/step1.md:
--------------------------------------------------------------------------------
1 | The Katacoda `terminal` layout provides a full Terminal experience. This can be extended to include a full IDE experience as a separate tab by including `showide` within the `environment` section of the `index.json`. For example:
2 |
3 | ```json
4 | "environment": {
5 | "showide": true
6 | }
7 | ```
8 |
9 | ## IDE Functionality
10 |
11 | With the IDE, you can open files certain files from Markdown - `newFile.js`{{open}}
12 |
13 | You can copy, extend or replace text from UI helpers.
14 |
15 | var http = require('http');
16 | var requestListener = function (req, res) {
17 | res.writeHead(200);
18 | res.end('Hello, World!');
19 | }
20 |
21 | var server = http.createServer(requestListener);
22 | server.listen(3000, function() { console.log("Listening on port 3000")});
23 |
24 |
25 | The following snippet will prepend the contents of the editor:
26 |
27 | console.log("Starting...")
28 |
29 |
30 | Within the Markdown, include:
31 |
32 |
33 | <pre class="file" data-filename="app.js" data-target="prepend">console.log("Starting...")
34 | </pre>
35 |
36 |
37 | The following snippet will append the contents of the editor:
38 |
39 | console.log("Finishing...")
40 |
41 |
42 | Within the Markdown, include:
43 |
44 |
45 | <pre class="file" data-filename="app.js" data-target="append">console.log("Finishing...")
46 | </pre>
47 |
48 |
49 | The editor can copy to particular files based on the data-filename attribute:
50 |
51 | console.log("Index.js here...")
52 |
53 |
54 | Within the Markdown, include:
55 |
56 |
57 | <pre class="file" data-filename="index.js" data-target="replace">console.log("Index.js here...")
58 | </pre>
59 |
60 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/.cypress/solver_spec.js.disabled:
--------------------------------------------------------------------------------
1 | describe('Complete Challenge with Solver', () => {
2 | const zenMoment=5000
3 |
4 | before(() => {
5 | cy.startScenario()
6 | });
7 |
8 | it('click start button', () => {
9 | cy.get('button[id="hide-intro"]').click()
10 | });
11 |
12 | it('is terminal ready', () => {
13 | cy.wait(zenMoment)
14 | cy.terminalShouldContain('Challenge ready.');
15 | });
16 |
17 | it('are dependencies healthy', () => {
18 | cy.terminalType('uname -a');
19 | cy.terminalShouldContain('Linux',{timeout: 500});
20 | cy.terminalShouldContain('Ubuntu',{timeout: 500});
21 |
22 | cy.terminalType('uptime');
23 | cy.terminalShouldContain('1 user',{timeout: 500});
24 | });
25 |
26 | it('open solutions', () => {
27 | expect(Cypress.env('test')).to.equal('See this?')
28 | cy.log("Found for TEST=" + Cypress.env('test'))
29 |
30 | cy.terminalType('solver solutions --decrypt ' + Cypress.env('SOLVER_KEY'));
31 | cy.terminalShouldContain('Solutions now available');
32 | });
33 |
34 | it('solve all', () => {
35 | cy.terminalType('solver all');
36 | cy.terminalShouldContain('Verification passed for task 1');
37 | });
38 |
39 | it('solve step 1', () => {
40 | cy.terminalType('solver next');
41 | cy.terminalShouldContain('Verification passed for task 1');
42 | });
43 |
44 | it('solve step 2', () => {
45 | cy.terminalType('solver next');
46 | cy.terminalShouldContain('Verification passed for task 2');
47 | });
48 |
49 | it('solve step 3', () => {
50 | cy.terminalType('solver next');
51 | cy.terminalShouldContain('Verification passed for task 3');
52 | });
53 |
54 | it('solve step 4', () => {
55 | cy.terminalType('solver next');
56 | cy.terminalShouldContain('Verification passed for task 4');
57 | });
58 |
59 | it('is complete', () => {
60 |
61 | // TODO: Problem with IFrames barriers with Cypress - may need a helper function instructionsShouldContain
62 | // cy.contains('Congratulations, you have successfully completed this challenge');
63 |
64 | // TODO: Also, are there plans for the finish page to return
65 |
66 | cy.terminalType('solver status --quiet');
67 | cy.terminalShouldContain('0');
68 | });
69 | })
70 |
--------------------------------------------------------------------------------
/environment-usages-pathway.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Katacoda Environments",
3 | "description": "Available Katacoda Environments",
4 | "icon": "fa-katacoda",
5 | "courses": [
6 | {
7 | "course_id": "ubuntu-1804",
8 | "title": "Ubuntu 18.04 Instance",
9 | "description": "General purpose environment"
10 | },
11 | {
12 | "course_id": "ubuntu-2004",
13 | "title": "Ubuntu 20.04 Instance",
14 | "description": "General purpose environment"
15 | },
16 | {
17 | "course_id": "kubernetes-cluster",
18 | "title": "Kubernetes Cluster",
19 | "description": "Multi node Kubernetes cluster configured using Kubeadm"
20 | },
21 | {
22 | "course_id": "kubernetes-cluster-running",
23 | "title": "Kubernetes Cluster Running",
24 | "description": "Multi node Kubernetes cluster configured using Kubeadm"
25 | },
26 | {
27 | "course_id": "minikube",
28 | "title": "Minikube",
29 | "description": "Use Minikube to help explain Kubernetes"
30 | },
31 | {
32 | "course_id": "java",
33 | "title": "Java / Scala",
34 | "description": ""
35 | },
36 | {
37 | "course_id": "kotlin",
38 | "title": "Kotlin",
39 | "description": ""
40 | },
41 | {
42 | "course_id": "python",
43 | "title": "Python 3",
44 | "description": ""
45 | },
46 | {
47 | "course_id": "nodejs",
48 | "title": "Node.js",
49 | "description": ""
50 | },
51 | {
52 | "course_id": "go",
53 | "title": "go",
54 | "description": ""
55 | },
56 | {
57 | "course_id": "rust",
58 | "title": "rust",
59 | "description": ""
60 | },
61 | {
62 | "course_id": "ruby",
63 | "title": "ruby",
64 | "description": ""
65 | },
66 | {
67 | "course_id": "dotnet",
68 | "title": "C# / Dotnet",
69 | "description": ""
70 | },
71 | {
72 | "course_id": "r",
73 | "title": "R",
74 | "description": ""
75 | },
76 | {
77 | "course_id": "bash",
78 | "title": "Bash",
79 | "description": ""
80 | }
81 | ]
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/custom-events/site/gulpfile.js:
--------------------------------------------------------------------------------
1 | const gulp = require('gulp');
2 | const browserSync = require('browser-sync');
3 | const fileinclude = require('gulp-file-include');
4 | const sass = require('gulp-sass');
5 | const server = browserSync.create();
6 | const request = require('request');
7 | const fs = require('fs')
8 | var SERVICE_URL='https://environment-events.katacoda.com';
9 |
10 | const paths = {
11 | html: {
12 | src: [ '**/*.html', '!node_modules', '!node_modules/**', '!dist/**'],
13 | dest: 'dist/'
14 | },
15 | sass: {
16 | src: ['*.scss'],
17 | dest: 'dist/'
18 | }
19 | };
20 |
21 | function html() {
22 | return gulp.src(paths.html.src)
23 | .pipe(gulp.dest(paths.html.dest));
24 | }
25 |
26 | function reload(done) {
27 | server.reload();
28 | done();
29 | }
30 |
31 | function fileInclude() {
32 | return gulp.src(['server/template.html'])
33 | .pipe(fileinclude({
34 | prefix: '@@',
35 | basepath: './'
36 | }))
37 | .pipe(gulp.dest(paths.html.dest + 'server'));
38 | }
39 |
40 | function style () {
41 | return gulp.src('./*.scss')
42 | .pipe(sass())
43 | .pipe(gulp.dest('./dist/css'));
44 | }
45 |
46 | function serve(done) {
47 | server.init({
48 | injectChanges: true,
49 | notify: false,
50 | server: {
51 | baseDir: './dist'
52 | },
53 | index: 'server/template.html'
54 | });
55 | done();
56 | }
57 |
58 |
59 | function callEventService(cb) {
60 | var s = fs.readFileSync('/tmp/socket','utf8')
61 | if (s){
62 | console.log ('Retrieved socket', s);
63 | const headers = {
64 | 'Content-Type': 'application/x-www-form-urlencoded'
65 | }
66 |
67 | const options = {
68 | url: SERVICE_URL,
69 | method: 'POST',
70 | headers: headers }
71 |
72 | request.post(options.url, {
73 | form: {
74 | message: 'reload-finished',
75 | socket: s
76 | }
77 | }, function(err, res) {
78 | if(err){
79 | console.log(err, res);
80 | }
81 | cb();
82 | });
83 | }
84 | }
85 |
86 | const watch = () => gulp.watch(paths.html.src, gulp.series(html, fileInclude, reload, callEventService));
87 | const sassWatch = () => gulp.watch(paths.sass.src, gulp.series(style, reload));
88 |
89 | const dev = gulp.series(html, fileInclude, style, serve, gulp.parallel(sassWatch, watch));
90 | exports.default = dev;
91 |
--------------------------------------------------------------------------------
/create-scenario-101/step3.md:
--------------------------------------------------------------------------------
1 | To help create the index.json and scenario structure, Katacoda has developed a command line interactive (CLI).
2 |
3 | ## Install CLI
4 |
5 | Install the CLI with the command `npm i katacoda-cli --global`{{execute}}.
6 |
7 | The commands follow the syntax is:
8 |
9 | `$ katacoda COMMAND`
10 |
11 | After the install has finished, the command can be run via `katacoda --help`{{execute}}.
12 |
13 | ## Create scenario
14 |
15 | For example, to create a new scenario you would run the command `katacoda scenarios:create`{{execute}}. The CLI will prompt you for a few questions in order to create your scenario:
16 |
17 | - **Friendly URL:** here you will type `test-scenario`. This attribute will determine the name of the folder of your scenario, and the URL to access it, so, should not contain spaces, should be lower case, etc. For example, if your username is *test-username* and your scenario was called *test-scenario* as suggested, the URL to point the scenario in the platform will be https://katacoda.com/test-username/scenarios/test-scenario/
18 | - **Title:** title the scenario
19 | - **Description:** description of the scenario, displayed on the intro screen
20 | - **Difficulty level:** provide users with a sense of the depth of content, displayed on the intro screen
21 | - **Estimated time:** provide users with an estimated time to complete, displayed on the intro screen
22 | - **Number of steps:** the numbers of the steps that the scenario will contain. The CLI will create all the template files for all the steps that you specified
23 | - **Image:** it will determine which base software will be available for your scenario. For example, if you need docker, java, go, etc as a pre-requisite. For more information read [Supported Environments](https://www.katacoda.community/essentials/environments.html)
24 | - **Layout:** it will determine the disposition of the elements of your scenario. For example, if you want to present only a terminal, or editor + terminal, etc. For more information read [Supported Layouts](https://www.katacoda.community/essentials/layouts.html)
25 |
26 | With this information, the CLI will create a folder with the name of the ***friendly URL*** introduced and will create inside of that folder the required files for your scenario.
27 |
28 | You can check your scenario created with this command:
29 |
30 | `ls test-scenario*`{{execute}}
31 |
--------------------------------------------------------------------------------
/challenges/challenge-hello-world/README.md:
--------------------------------------------------------------------------------
1 | # Challenge Template
2 |
3 | Hi! Thanks for opening this README. I am feeling optimistic, as you must be the sort of person who reads the directions before trying to build something new. We are going to get along great.
4 |
5 | This folder is a "Hello, world!" example of a challenge. Consider this a bare minimum, skeletal starting point, which you will customize to suit your own purposes.
6 |
7 | Here is a quick lay of the land:
8 |
9 | **index.json** --- This is the core definition of any scenario, challenge or otherwise. Note especially:
10 |
11 | - `"type": "challenge@0.8"` --- to specify this is a challenge, not a standard scenario
12 | - `"imageid": "ubuntu:2004"` --- This example uses the Ubuntu 20.04 base image, but you should use whatever [base image](https://www.katacoda.community/essentials/environments.html) is most appropriate for your challenge.
13 | - Note that the `index.json` includes a `verify` and `hint` for each step:
14 |
15 | ```json
16 | "steps": [
17 | {
18 | "title": "Bananas",
19 | "text": "1_task.md",
20 | "verify": "1_verify.sh",
21 | "hint": "1_hint.sh"
22 | },
23 | …
24 | ]
25 | ```
26 |
27 | The `verify` points to a shell script. This is run continuously until a success code (exit code 0) is returned.
28 |
29 | The `hint` is another shell script that defines what hints to display for that task/step and when (that is, under what conditions).
30 |
31 | **.md files** --- The Markdown files are standard Markdown, just as used for standard scenarios. Not all the Katacoda Markdown extensions can be used in this context, however (such as clickable code shortcuts).
32 |
33 | **.sh files** --- Bash shell scripts, as described above.
34 |
35 | For clarity, we strongly recommend following the naming convention here in this example, whereby files are prefixed with the related task/step number:
36 |
37 | ```sh
38 | 1_hint.sh
39 | 1_task.md
40 | 1_verify.sh
41 | 2_hint.sh
42 | 2_task.md
43 | 2_verify.sh
44 | ```
45 |
46 | **SOLUTIONS.md** --- For each task, please provide instructions for the solution. This is _not_ shared with the learner, but is for our internal reference, to help with testing.
47 |
48 | Please also review the [challenge documentation here](https://www.katacoda.community/challenges/challenges.html).
49 |
50 | _Please reach out with feedback and questions about this example! And thank you for reading the directions._ ♥️
51 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/assets/verifications.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This script is called by the solver utility. Each function should be named
4 | # verify_task_#, where # is the number of the task each function will solve.
5 | # When a verification fails for a step, the error number returned corresponds
6 | # to the hint number found in hints.md.
7 |
8 | function verify_task_1() {
9 | dpkg-query -l | grep cowsay
10 | if [[ $? -ne 0 ]]
11 | then
12 | return 1
13 | fi
14 |
15 | whereis cowsay
16 | if [[ $? -ne 0 ]]
17 | then
18 | return 2
19 | fi
20 |
21 |
22 | if [[ ! -f 'cowsay.txt' ]]
23 | then
24 | return 3
25 | fi
26 |
27 | cowsay "O'Reilly, inspiring the future for more than 40 years" > '/tmp/expected-cowsay.txt'
28 | diff '/tmp/expected-cowsay.txt' 'cowsay.txt'
29 | if [[ $? -ne 0 ]]
30 | then
31 | return 4
32 | fi
33 | }
34 |
35 |
36 | function verify_task_2() {
37 | if [[ ! -f 'kinder-passwd.txt' ]]
38 | then
39 | return 1
40 | fi
41 |
42 | cat /etc/passwd | column -t -s : > '/tmp/expected-format.txt'
43 | diff '/tmp/expected-format.txt' 'kinder-passwd.txt'
44 | if [[ $? -ne 0 ]]
45 | then
46 | return 2
47 | fi
48 | }
49 |
50 |
51 | function verify_task_3() {
52 |
53 | dpkg-query -l | grep poppler-utils
54 |
55 | if [[ $? -ne 0 ]]
56 | then
57 | return 1
58 | fi
59 |
60 | whereis pdftotext
61 | if [[ $? -ne 0 ]]
62 | then
63 | return 2
64 | fi
65 |
66 | if [[ ! -f 'streetcar.txt' ]]
67 | then
68 | return 3
69 | fi
70 |
71 | grep 'on a street in New Orleans' streetcar.txt
72 | if [[ $? -ne 0 ]]
73 | then
74 | return 4
75 | fi
76 |
77 | if [[ ! -f 'stella.properties' ]]
78 | then
79 | return 5
80 | fi
81 |
82 | grep 'stella=' stella.properties
83 | if [[ $? -ne 0 ]]
84 | then
85 | return 6
86 | fi
87 |
88 | grep 'stella_exclamation=' stella.properties
89 | if [[ $? -ne 0 ]]
90 | then
91 | return 7
92 | fi
93 |
94 | grep 'stella=155' stella.properties
95 | if [[ $? -ne 0 ]]
96 | then
97 | return 8
98 | fi
99 |
100 | grep 'stella_exclamation=16' stella.properties
101 | if [[ $? -ne 0 ]]
102 | then
103 | return 9
104 | fi
105 | }
106 |
107 |
108 | function verify_task_4() {
109 |
110 | if [[ ! -f 'my-xargs.sh' ]]
111 | then
112 | return 1
113 | fi
114 |
115 | if [[ $(grep -c 'xargs' my-xargs.sh) -ne 1 ]]
116 | then
117 | return 2
118 | fi
119 |
120 | echo -n "1 2 3 4 5" | xargs -d ' ' -i ls {}.txt
121 | if [[ $? -ne 0 ]]
122 | then
123 | return 3
124 | fi
125 |
126 | echo -n "1 2 3 4 5" | xargs -d ' ' -i grep "I am {}" {}.txt
127 | if [[ $? -ne 0 ]]
128 | then
129 | return 4
130 | fi
131 | }
132 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/init-background.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Log script activity (https://serverfault.com/a/103569)
4 | exec 3>&1 4>&2
5 | trap 'exec 2>&4 1>&3' 0 1 2 3
6 | exec 1>/var/log/init-background.log 2>&1
7 | set -x
8 |
9 | # Add any installations or configurations here that the scenario may need
10 | #
11 |
12 | # Common curl switches
13 | echo '-s' >> ~/.curlrc
14 |
15 | # =========================
16 | # Do not modify lines below
17 |
18 | # The Solver command-line tool cannot be committed to each challenge and installed via assets. Mostly because
19 | # scenarios limit the asset size to below 10Mb. Instead, this script downloads the solver utility from a public source.
20 | # The version of the solver release should be the exact version the author testing the scenario with. Authors are
21 | # encourage to periodically upgrade the solver release when new ones are available.
22 | #
23 | # Each time a scenario starts, this script is called from the background script (init-background.sh). If the solver
24 | # download fails the scenario will not work correctly. To greatly reduce this risk, the download of
25 | # the same version of solver comes from two independent sources:
26 | #
27 | # 1) GitHub container registry
28 | # 2) GitHub solver release page
29 | #
30 | # The two sources are still related to GitHub, but they are different storage services. The latter is less reliable
31 | # than the container registry.
32 |
33 | # The 'solver create' command will install this script and ensure this version matches the utilized solver version.
34 | SOLVER_VERSION=0.5.4
35 |
36 | function verify_solver_install() {
37 | solver --version | grep $SOLVER_VERSION
38 | return $?
39 | }
40 |
41 | # First download request - download container image, create it, copy solver out of the container, delete container
42 | SOLVER_CONTAINER_IMAGE=ghcr.io/javajon/solver
43 | ID=$(docker create $SOLVER_CONTAINER_IMAGE:$SOLVER_VERSION)
44 | docker cp "$ID":/application /usr/local/bin/solver
45 |
46 | verify_solver_install
47 | if [ $? -ne 0 ]; then
48 | echo "Failed to download solver from $SOLVER_CONTAINER_IMAGE:$SOLVER_VERSION"
49 |
50 | # Second download request - download solver linux binary from release page
51 | RELEASE="https://github.com/javajon/katacoda-solver/releases/download/$SOLVER_VERSION/solver-$SOLVER_VERSION-runner"
52 | wget -q -O solver $RELEASE
53 | chmod +x solver
54 | mv solver /usr/local/bin/
55 |
56 | verify_solver_install
57 | if [ $? -ne 0 ]; then
58 | echo "Failed to download solver from github release page: $RELEASE"
59 | # init-foreground displays messsage on linux prompt that there is a problem.
60 | fi
61 | fi
62 |
63 | # Signal to challenge controller that the startup is complete
64 | echo 'done' > /opt/katacoda-background-finished
65 |
66 | # Signal to init-foreground.sh that the startup is complete
67 | echo 'done' > /opt/.backgroundfinished
68 |
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/init-foreground.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | FREQUENCY=1 # Delay between each check for completion
4 | BACKGROUND_SIGNAL_FILE='/opt/.backgroundfinished' # File updated by background to indicate completion
5 | BACKGROUND_SAFE_WORD='done' # Word in BACKGROUND_SIGNAL_FILE indicating completion
6 | START_MESSAGE='Starting scenario' # Message before the progress animation
7 | END_NORMAL_MESSAGE='Challenge ready.'
8 | END_KILLED_MESSAGE='Interupted. This scenario may still be initializing.'
9 |
10 | SPINNER_COLOR_NUM=2 # Color to use, unless COLOR_CYCLE=1
11 | SPINNER_COLOR_CYCLE=0 # 1 to rotate colors between each animation
12 | INTERVAL=.25
13 | SPINNER_NORMAL=$(tput sgr0) # Reset encoding
14 | symbols=("▐⠂ ▌" "▐⠈ ▌" "▐ ⠂ ▌" "▐ ⠠ ▌" "▐ ⡀ ▌" "▐ ⠠ ▌" "▐ ⠂ ▌" "▐ ⠈ ▌" "▐ ⠂ ▌" "▐ ⠠ ▌" "▐ ⡀ ▌" "▐ ⠠ ▌" "▐ ⠂ ▌" "▐ ⠈ ▌" "▐ ⠂▌" "▐ ⠠▌" "▐ ⡀▌" "▐ ⠠ ▌" "▐ ⠂ ▌" "▐ ⠈ ▌" "▐ ⠂ ▌" "▐ ⠠ ▌" "▐ ⡀ ▌" "▐ ⠠ ▌" "▐ ⠂ ▌" "▐ ⠈ ▌" "▐ ⠂ ▌" "▐ ⠠ ▌" "▐ ⡀ ▌" "▐⠠ ▌")
15 |
16 | progress_pid=0
17 |
18 | cleanup () {
19 | kill $progress_pid >/dev/null 2>&1
20 | progress_pid=-1
21 | end_message=$END_KILLED_MESSAGE
22 | }
23 |
24 | show_progress () {
25 | while :; do
26 | tput civis
27 | for symbol in "${symbols[@]}"; do
28 | if [ $SPINNER_COLOR_CYCLE -eq 1 ]; then
29 | if [ $SPINNER_COLOR_NUM -eq 7 ]; then
30 | SPINNER_COLOR_NUM=1
31 | else
32 | SPINNER_COLOR_NUM=$((SPINNER_COLOR_NUM+1))
33 | fi
34 | fi
35 | local color
36 | color=$(tput setaf ${SPINNER_COLOR_NUM})
37 | tput sc
38 | printf "%s%s%s " "${color}" "${symbol}" "${SPINNER_NORMAL}"
39 | tput rc
40 | sleep "${INTERVAL}"
41 | done
42 | done
43 | tput cnorm
44 | return 0
45 | }
46 |
47 | start_progress () {
48 | show_progress &
49 | progress_pid=$!
50 |
51 | # Catch any exit and stop progress animation
52 | trap cleanup SIGINT EXIT INT QUIT TERM
53 |
54 | clear && echo -n "$START_MESSAGE "
55 |
56 | # Periodically check for background signal or user Ctrl-C interuption
57 | end_message=$END_NORMAL_MESSAGE
58 | while [[ $progress_pid -ge 0 ]]; do
59 | grep -i ${BACKGROUND_SAFE_WORD} ${BACKGROUND_SIGNAL_FILE} &> /dev/null
60 | if [[ "$?" -eq 0 ]]; then
61 | kill $progress_pid >/dev/null 2>&1
62 | break
63 | fi
64 | sleep ${FREQUENCY}
65 | done
66 |
67 | stty sane; tput cnorm; clear
68 | solver --version | grep "version">/dev/null 2>&1
69 | if [ $? -ne 0 ]; then
70 | printf "This challenge did not appear to start correctly. Refresh this browser page to try again.\n\n"
71 | else
72 | printf "%s\n\n" "${end_message}"
73 | fi
74 |
75 | # Pick up any changes during background
76 | source ~/.bashrc
77 |
78 | # In shell context for student, so set any environment variables for learner here, if needed:
79 | }
80 |
81 | clear && start_progress
--------------------------------------------------------------------------------
/challenges/challenge-linux-solver/assets/hints.md:
--------------------------------------------------------------------------------
1 | # Task Hints Authoring Guidelines
2 |
3 | When authoring this collection of hints, mark each hint with the double pound (##) markdown header and title the header with Task n, Hint n. Capital T and H. Tasks and Hints nymbers start at 1. See this [Challenge Hints](https://www.katacoda.community/challenges.html#ui-example) in the authoring guide on how to provide hints for your challenge scenarios. The [Katacoda markdown extensions](https://www.katacoda.community/scenario-syntax.html#katacoda-s-markdown-extensions) can be applied in this markdown. An optional horizontal divider (`---`) can be added before each Task section.
4 |
5 | ---
6 |
7 | ## Task 1, Hint 1
8 |
9 | The latest version of `cowsay` may be herded into this challenge using apt-get. The package manager is not reporting cowsay is installed.
10 |
11 | ## Task 1, Hint 2
12 |
13 | The `cowsay` utility is not appearing in the `whereis` $PATH.
14 |
15 | ## Task 1, Hint 3
16 |
17 | The expected `cowsay.txt` file has not been created yet.
18 |
19 | ## Task 1, Hint 4
20 |
21 | The `cowsay.txt` file is present but does not contain the expected message formatted to the bovine culture as outputted by the cowsay utility.
22 |
23 | ---
24 |
25 | ## Task 2, Hint 1
26 |
27 | The expected `kinder-passwd.txt` file has not been created yet.
28 |
29 | ## Task 2, Hint 2
30 |
31 | The `kinder-passwd.txt` file does container the passwd data in its expected columnar form. Have you discovered the ['column'](https://man7.org/linux/man-pages/man1/column.1.html) utility yet?
32 |
33 | ---
34 |
35 | ## Task 3, Hint 1
36 |
37 | The apt package manager is not reporting poppler-utils is installed.
38 |
39 | ## Task 3, Hint 2
40 |
41 | The `pdf2text` utility is not appearing in the `whereis` $PATH.
42 |
43 | ## Task 3, Hint 3
44 |
45 | The expected `streetcar.txt` file has not been created yet.
46 |
47 | ## Task 3, Hint 4
48 |
49 | The `streetcar.txt` file does not contain the expect text from the script found in the PDF. Did you use pdf2text to fill the text file?
50 |
51 | ## Task 3, Hint 5
52 |
53 | The expected `stella.properties` file has not been created yet.
54 |
55 | ## Task 3, Hint 6
56 |
57 | The `stella.properties` file does not have a setting for `stella=`.
58 |
59 | ## Task 3, Hint 7
60 |
61 | The `stella.properties` file does not have a setting for `stella_exclamation=`.
62 |
63 | ## Task 3, Hint 8
64 |
65 | The `stella.properties` file does not have the correct value for `stella=?`.
66 |
67 | ## Task 3, Hint 9
68 |
69 | The `stella.properties` file does not have the correct value for `stella_exclamation=?`.
70 | ---
71 |
72 | ## Task 4, Hint 1
73 |
74 | The expected `my-xargs.sh` file has not been created yet.
75 |
76 | ## Task 4, Hint 2
77 |
78 | The `my-xargs.sh` should just have one usage of `xargs` in a single line.
79 |
80 | ## Task 4, Hint 3
81 |
82 | There should be 5 files present called 1.txt, 2.txt, 3.txt, 4.txt, and 5.txt.
83 |
84 | ## Task 4, Hint 4
85 |
86 | Each one of the 5 text files should contain the line "I am n", where n is the matching number in the file name. Did you consider this form `echo -n '1 2 3 4 5' | xargs -d ' ' -i sh -c "touch {}.txt ; echo 'I am {}' > {}.txt"`?
87 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-vscode-terminal/step1.md:
--------------------------------------------------------------------------------
1 | The Katacoda `editor-terminal` UI Layout combines an Editor with a Terminal. When changes are made to files using the editor, they are automatically sync'ed to the user's home directory.
2 |
3 | # Index.json
4 |
5 | The syntax for the editor is automatically inferred from the file extension. This can be enforced by including a "uisettings"
6 |
7 |
8 | "environment": {
9 | "uilayout": "vscode-terminal-split"
10 | },
11 |
12 |
13 | # Helper Functionality
14 |
15 | To support users, Katacoda has integrated various features to help users.
16 |
17 | Copy file to editor:
18 |
19 | var http = require('http');
20 | var requestListener = function (req, res) {
21 | res.writeHead(200);
22 | res.end('Hello, World!');
23 | }
24 |
25 | var server = http.createServer(requestListener);
26 | server.listen(3000, function() { console.log("Listening on port 3000")});
27 |
28 |
29 | The following snippet will prepend the contents of the editor:
30 |
31 | console.log("Starting...")
32 |
33 |
34 | Within the Markdown, include:
35 |
36 |
37 | <pre class="file" data-filename="app.js" data-target="prepend">console.log("Starting...")
38 | </pre>
39 |
40 |
41 | The following snippet will append the contents of the editor:
42 |
43 | console.log("Finishing...")
44 |
45 |
46 | Within the Markdown, include:
47 |
48 |
49 | <pre class="file" data-filename="app.js" data-target="append">console.log("Finishing...")
50 | </pre>
51 |
52 |
53 | The editor can copy to particular files based on the data-filename attribute:
54 |
55 | console.log("Index.js here...")
56 |
57 |
58 | Within the Markdown, include:
59 |
60 |
61 | <pre class="file" data-filename="index.js" data-target="replace">console.log("Index.js here...")
62 | </pre>
63 |
64 |
65 | Using the `data-target="insert"` attribute you can instruct the editor to insert the text in a particular position marked by `data-marker` attribute.
66 |
67 | #TODO-insert 68 |69 | 70 |
71 | <pre class="file" data-filename="app.js" data-target="insert" data-marker="#TODO-insert">
72 | console.log("Inserted value using the data-marker attribute...")
73 | </pre>
74 |
75 |
76 |
77 | console.log("Inserted value using the data-marker attribute...")
78 |
79 |
80 | ## New Files
81 |
82 | Create a new file via the CLI:
83 | `touch newFile.js`{{execute}}
84 |
85 | The Markdown is:
86 | `touch newFile.js`{{execute}}
87 |
88 | This can then be opened:
89 | `newFile.js`{{open}}
90 |
91 | The Markdown is:
92 | `newFile.js`{{open}}
93 |
--------------------------------------------------------------------------------
/uilayouts/uilayout-editor-terminal/step1.md:
--------------------------------------------------------------------------------
1 | The Katacoda `editor-terminal` UI Layout combines an Editor with a Terminal.
2 | When changes are made to files using the editor, they are automatically sync'ed
3 | to the user's home directory.
4 |
5 | # Index.json
6 |
7 | The editor will display and interact with files relative to the `uieditorpath`
8 | you set in the `index.json` file for your scenario.
9 |
10 |
11 | "environment": {
12 | "uilayout": "editor-terminal",
13 | "uieditorpath": "/root/example"
14 | }
15 |
16 |
17 | If not specified, the `uieditorpath` will default to the `/root` directory.
18 |
19 | You can pre-open a set of files in the editor by defining a `files` array in the root
20 | of the `index.json` file.
21 |
22 | 23 | "files": [ 24 | "app.js", "index.js" 25 | ] 26 |27 | 28 | Files open in the editor window are updated as you make changes in the editor. 29 | If the files do not already exist, the tabs will still open for them, but they 30 | will not be created until you have updated the content in the editor window. 31 | 32 | You can include a path with these filenames. Paths should be given 33 | relative to the `uieditorpath`. Only files under that path will be 34 | diplayed in the editor's directory tree. 35 | 36 | The syntax highlighting for the editor is automatically inferred from the file 37 | extension. This can be enforced by adding a `uisettings` to your environment. 38 | 39 |
40 | "environment": {
41 | "uilayout": "editor-terminal",
42 | "uieditorpath": "/root/example",
43 | "uisettings": "javascript"
44 | },
45 |
46 |
47 | # Helper Functionality
48 |
49 | You can replace, prepend, append, and insert text into an open editor file.
50 |
51 | Copy file to editor:
52 |
53 | var http = require('http');
54 | var requestListener = function (req, res) {
55 | res.writeHead(200);
56 | res.end('Hello, World!');
57 | }
58 |
59 | var server = http.createServer(requestListener);
60 | server.listen(3000, function() { console.log("Listening on port 3000")});
61 |
62 |
63 | The following snippet will prepend the contents of the editor:
64 |
65 | console.log("Starting...")
66 |
67 |
68 | Within the Markdown, include:
69 |
70 |
71 | <pre class="file" data-filename="app.js" data-target="prepend">console.log("Starting...")
72 | </pre>
73 |
74 |
75 | The following snippet will append the contents of the editor:
76 |
77 | console.log("Finishing...")
78 |
79 |
80 | Within the Markdown, include:
81 |
82 |
83 | <pre class="file" data-filename="app.js" data-target="append">console.log("Finishing...")
84 | </pre>
85 |
86 |
87 | The editor can copy to particular files based on the data-filename attribute:
88 |
89 | console.log("Index.js here...")
90 |
91 |
92 | Within the Markdown, include:
93 |
94 |
95 | <pre class="file" data-filename="index.js" data-target="replace">console.log("Index.js here...")
96 | </pre>
97 |
98 |
99 | Using the `data-target="insert"` attribute you can instruct the editor to insert the text in a particular position marked by `data-marker` attribute.
100 |
101 | #TODO-insert 102 |103 | 104 |
105 | <pre class="file" data-filename="app.js" data-target="insert" data-marker="#TODO-insert">
106 | console.log("Inserted value using the data-marker attribute...")
107 | </pre>
108 |
109 |
110 |
111 | console.log("Inserted value using the data-marker attribute...")
112 |
113 |
114 | ## New Files
115 |
116 | Added files in the `uieditorpath` will automatically be added to the tree in the
117 | editor. A markdown extension also makes it easy to open new files in the editor.
118 |
119 | Create a new file via the CLI:
120 | `touch newFile.js`{{execute}}
121 |
122 | The Markdown is:
123 | `touch newFile.js`{{execute}}
124 |
125 | This can then be opened:
126 | `newFile.js`{{open}}
127 |
128 | The Markdown is:
129 | `newFile.js`{{open}}
130 |
--------------------------------------------------------------------------------
/markdown-extensions/step1.md:
--------------------------------------------------------------------------------
1 | Katacoda has extended Markdown to simplify the users interaction while completing the scenarios and encounter less mistakes.
2 |
3 | ## Copy to Clipboard
4 |
5 | This extension will copy the command or text to the clipboard.
6 |
7 | Markdown:
8 | `echo "Copy to Clipboard"`{{copy}}
9 |
10 | Results:
11 | `echo "Copy to Clipboard"`{{copy}}
12 |
13 | ## Multi-Line Copy to Clipboard
14 |
15 | The same functionality as above is available over multiple lines.
16 |
17 | Markdown:
18 |
19 | ```sh
20 | echo "Line 1"
21 |
22 | echo "Line 2"
23 |
24 | echo "Line 3"
25 | ```{{copy}}
26 |
27 |
28 | Results:
29 | ```sh
30 | echo "Line 1"
31 |
32 | echo "Line 2"
33 |
34 | echo "Line 3"
35 | ```{{copy}}
36 |
37 | ## Execute in Terminal
38 |
39 | Katacoda has integration to automatically execute the commands for the terminal.
40 |
41 | This is done by adding `execute` to the markdown code block, for example:
42 | `echo "Run in Terminal"`{{execute}}
43 |
44 | This creates:
45 | `echo "Run in Terminal"`{{execute}}
46 |
47 | ## Multi-Line Execute in Terminal
48 |
49 | The same functionality as above is available over multiple lines.
50 |
51 |
52 | ```sh
53 | echo "Line 1"
54 |
55 | echo "Line 2"
56 |
57 | echo "Line 3"
58 | ```{{execute}}
59 |
60 |
61 |
62 | This creates:
63 | ```sh
64 | echo "Line 1"
65 |
66 | echo "Line 2"
67 |
68 | echo "Line 3"
69 | ```{{execute}}
70 |
71 | ## Interrupt
72 |
73 | When the user has long running commands, such as a watch, it can be useful to ensure that this is stopped but the user runs the next command.
74 |
75 | `echo "Send Ctrl+C before running Terminal"`{{execute interrupt}}
76 |
77 | `echo "Send Ctrl+C before running Terminal"`{{execute interrupt}}
78 |
79 | ## Interrupt
80 |
81 | When the user has long running commands, such as `top`{{execute}}, it can be useful to ensure that this is stopped but the user runs the next command.
82 |
83 | `echo "Send Ctrl+C before running Terminal"`{{execute interrupt}}
84 |
85 | `echo "Send Ctrl+C before running Terminal"`{{execute interrupt}}
86 |
87 | ## Control Sequences
88 |
89 | Alongside the interrupt command above, certain Control Sequences can be sent.
90 |
91 | Given a long running command, like `top`{{execute}}. It can be stopped using Ctrl+C. This can be executed as a control sequence with the command `^C`{{execute ctrl-seq}}
92 |
93 | The markdown for this is:
94 |
95 | `^C`{{execute ctrl-seq}}
96 |
97 |
98 | The use of control sequences can be useful when teaching applications such as `vim`{{execute}}.
99 |
100 | The instructions can guide the user on how
101 |
102 | * Switch to insert mode by typing `i`{{execute no-newline}}
103 |
104 | * Once finished, press ESC (`^ESC`{{execute ctrl-seq}}) to switch back to normal mode
105 |
106 | * To exit, type `:q!`{{execute}}
107 |
108 | In the markdown, you would include:
109 |
110 | `i`{{execute no-newline}}
111 |
112 | `^ESC`{{execute ctrl-seq}}
113 |
114 | `:q!`{{execute}}
115 |
116 |
117 | Notice the use of `no-newline` as a way to send a keystroke with a carriage return following it.
118 |
119 | ## Keyboard Icons
120 |
121 | This can also be helped by using Keyboard symbols to show users to use Ctrl+C
122 |
123 | The Markdown is:
124 | 125 | <kbd>Ctrl</kbd>+<kbd>C</kbd> 126 |127 | 128 | 129 | ## Execute on different hosts 130 | 131 | When using the `terminal-terminal` layout and multiple hosts within the cluster, you can have commands executed on which host is required. This is used within our [Kubernetes scenarios](https://www.katacoda.com/courses/kubernetes/getting-started-with-kubeadm). 132 | 133 |
134 | `echo "Run in Terminal Host 1"`{{execute HOST1}}
135 |
136 | `echo "Run in Terminal Host 2"`{{execute HOST2}}
137 |
138 |
139 | `echo "Run in Terminal Host 1"`{{execute HOST1}}
140 |
141 | `echo "Run in Terminal Host 2"`{{execute HOST2}}
142 |
143 | ## Execute in different Terminal windows
144 |
145 | When explaining complex systems, it can be useful to run commands in a separate terminal window. This can be run automatically by including the target Terminal number.
146 |
147 | If the terminal is not open, it will launch and the command will be executed.
148 |
149 |
150 | `echo "Run in Terminal 3"`{{execute T3}}
151 |
152 | `echo "Open and Execute in Terminal 4"`{{execute T4}}
153 |
154 |
155 |
156 | `echo "Run in Terminal 3"`{{execute T3}}
157 |
158 | `echo "Open and Execute in Terminal 4"`{{execute T4}}
159 |
--------------------------------------------------------------------------------
/profile-pathway.json:
--------------------------------------------------------------------------------
1 | {
2 | "courses": [
3 | {
4 | "title": "Katacoda UI Layouts Example",
5 | "description": "Example of different Katacoda UI Layouts. Also, an example of creating a Katacoda Course!",
6 | "id": "uilayouts"
7 | },
8 | {
9 | "title": "Katacoda Environments",
10 | "description": "Example of selective available Katacoda Environments. More examples coming soon!",
11 | "id": "environment-usages"
12 | },
13 | {
14 | "title": "Katacoda Challenges",
15 | "description": "Examples of how to create Challenges on Katacoda",
16 | "id": "challenges"
17 | }
18 | ],
19 | "scenarios": [
20 | {
21 | "title": "Creating Your First Katacoda Scenario",
22 | "description": "Learn how to create your first Katacoda scenario",
23 | "id": "create-scenario-101"
24 | },
25 | {
26 | "title": "Creating a Katacoda Course",
27 | "description": "Learn how to create a collection of Katacoda scenarios into a course",
28 | "id": "create-course"
29 | },
30 | {
31 | "title": "Markdown Extensions",
32 | "description": "Examples of how the Katacoda Markdown Extensions",
33 | "id": "markdown-extensions"
34 | },
35 | {
36 | "title": "Katacoda Placeholders for IP addresses and URLs",
37 | "description": "Use Placeholders to dynamically replace values within your scenario content",
38 | "id": "placeholders"
39 | },
40 | {
41 | "title": "Clipboard Integration",
42 | "description": "Understand how the Katacoda Clipboard integration",
43 | "id": "clipboard"
44 | },
45 | {
46 | "title": "Interactive Quiz",
47 | "description": "Verify understand and key points by using an interactive quiz",
48 | "id": "quiz"
49 | },
50 | {
51 | "title": "Run Commands Automatically",
52 | "description": "Automatically run commands in the foreground and background when a scenario starts",
53 | "id": "run-commands-automatically"
54 | },
55 | {
56 | "title": "Displaying Progress Spinner",
57 | "description": "Display a friendly spinner while long running jobs are completed",
58 | "id": "displaying-progress-spinner"
59 | },
60 | {
61 | "title": "Verifying user actions during scenario",
62 | "description": "Run checks to make sure the user can proceed. Scenario also offers user solutions if they struggle",
63 | "id": "verified-steps"
64 | },
65 | {
66 | "title": "Displaying Web UI",
67 | "description": "Display a Web UI running on the environment",
68 | "id": "displaying-web-ui"
69 | },
70 | {
71 | "title": "Dashboard and Terminal Tabs",
72 | "description": "Add Additional Tabs to the UI",
73 | "id": "dashboard-tabs"
74 | },
75 | {
76 | "title": "Dashboard and Terminal Tabs (IFrame)",
77 | "description": "Add Additional Tabs to the UI with iFrame integration",
78 | "id": "dashboard-tabs-iframe"
79 | },
80 | {
81 | "title": "Visualise Environment Tab",
82 | "description": "Learn how to add the Visualise Tab",
83 | "id": "visualise-docker"
84 | },
85 | {
86 | "title": "Upload File Assets",
87 | "description": "Automatically upload file assets to the environment",
88 | "id": "upload-assets"
89 | },
90 | {
91 | "title": "Displaying Images",
92 | "description": "Display images as part of the scenario",
93 | "id": "displaying-images"
94 | },
95 | {
96 | "title": "Embed Videos into Scenarios",
97 | "description": "Learn how to embed YouTube videos into a scenario",
98 | "id": "embed-videos"
99 | },
100 | {
101 | "title": "Credit where credit due",
102 | "description": "Possible ways to provide credit for the content or ideas within the scenario",
103 | "id": "credit-where-credit-due"
104 | },
105 | {
106 | "title": "Writing files",
107 | "description": "How to write a file in your scenario",
108 | "id": "writing-files"
109 | },
110 | {
111 | "title": "Exclusion patterns",
112 | "description": "Exclude files or directories of your scenario",
113 | "id": "exclusion-patterns"
114 | },
115 | {
116 | "title": "Visual Studio Code - Install extensions",
117 | "description": "Install VS Code extensions using background scripts",
118 | "id": "vscode-extensions"
119 | }
120 | ]
121 | }
122 |
--------------------------------------------------------------------------------