Skip to content

Commit 62f4b3c

Browse files
Merge pull request #3 from educates/develop
New blog post
2 parents 033d043 + 96b78e0 commit 62f4b3c

16 files changed

+1788
-1407
lines changed

.devcontainer/devcontainer.json

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,29 @@
11
{
22
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-22.04",
3-
"settings": {
4-
"[typescript]": {
5-
"editor.defaultFormatter": "esbenp.prettier-vscode",
6-
"editor.formatOnSave": true
7-
},
8-
"[json]": {
9-
"editor.defaultFormatter": "esbenp.prettier-vscode",
10-
"editor.formatOnSave": true
11-
},
12-
"[jsonc]": {
13-
"editor.defaultFormatter": "esbenp.prettier-vscode",
14-
"editor.formatOnSave": true
3+
"customizations": {
4+
"vscode": {
5+
"settings": {
6+
"[typescript]": {
7+
"editor.defaultFormatter": "esbenp.prettier-vscode",
8+
"editor.formatOnSave": true
9+
},
10+
"[json]": {
11+
"editor.defaultFormatter": "esbenp.prettier-vscode",
12+
"editor.formatOnSave": true
13+
},
14+
"[jsonc]": {
15+
"editor.defaultFormatter": "esbenp.prettier-vscode",
16+
"editor.formatOnSave": true
17+
}
18+
},
19+
"extensions": [
20+
"dbaeumer.vscode-eslint",
21+
"orta.vscode-jest",
22+
"esbenp.prettier-vscode",
23+
"streetsidesoftware.code-spell-checker"
24+
]
1525
}
1626
},
17-
"extensions": [
18-
"dbaeumer.vscode-eslint",
19-
"orta.vscode-jest",
20-
"esbenp.prettier-vscode",
21-
"streetsidesoftware.code-spell-checker"
22-
],
2327
// "forwardPorts": [3000],
2428
// "containerUser": "vscode",
2529
"postCreateCommand": "yarn install",

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,23 @@ $ GIT_USER=<Your GitHub username> yarn deploy
3939
```
4040

4141
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
42+
43+
44+
## Publishing workflow for GitHub
45+
46+
- Develop your code in branch `develop`
47+
- Once you're done with your changes, commit them, push them and create a PR to incorporate the changes in `main`.
48+
```
49+
git commit -m "Message"
50+
git push origin develop
51+
```
52+
- There's a workflow that will test that everything builds fine, and if everything is ok, you should be able to merge the PR into `main`
53+
- There's a workflow that for the merged PR into `main` will also publish the generated site to `github pages` so that will become online
54+
- You need to pull down `main` branch and merge it into `develop` locally for your next iteration.
55+
```
56+
git checkout main
57+
git pull
58+
git checkout develop
59+
git merge main
60+
git push
61+
```

blog/2024-10-13-announcement.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
slug: educates-independent
33
title: Educates becomes an independent OSS project
4-
authors: [jorge]
4+
authors: [jorge,graham]
55
tags: [educates, announcement]
66
---
77
I am happy to announce a significant change regarding the [Educates project](https://educates.dev), an interactive training platform for hands-on labs hosted on Kubernetes.
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
---
2+
slug: your-first-workshop
3+
title: Your first workshop
4+
authors: [jorge]
5+
tags: [getting-started, authoring, educates]
6+
---
7+
8+
Now that you have [created your local cluster](/blog/getting-started-on-kind) it's time to create your first
9+
workshop.
10+
11+
Educates CLI provides commands to get you started, and provides you with a skeleton of a workshop. This
12+
workshop is not going to have any addition, but we will be augmenting this workshop's content and capabilities
13+
in later posts.
14+
15+
<!-- truncate -->
16+
17+
Let's get started and create the skeleton of our workshop:
18+
19+
```bash
20+
educates new-workshop my-workshop --name "my-workshop" --title "My first workshop" --description "First workshop using Educates"
21+
```
22+
23+
In the above command we first, give it a path `my-workshop` that will be created on our current directory
24+
where all the workshop files will be created. Then, we provide a few flags that will be part of our
25+
workshop definition.
26+
27+
If we list the contents of this new folder, we see everything that's being created out of the box:
28+
29+
```bash
30+
$ tree my-workshop
31+
my-workshop
32+
├── README.md
33+
├── resources
34+
│   └── workshop.yaml
35+
└── workshop
36+
├── config.yaml
37+
└── content
38+
├── 00-workshop-overview.md
39+
├── 01-workshop-instructions.md
40+
└── 99-workshop-summary.md
41+
```
42+
43+
The definition of the workshop is the `resources/workshop.yaml` file. This is a Custom Resource Definition
44+
(CRD) of kind `Workshop` and apiVersion `training.educates.dev/v1beta1`. This is the key file for Educates as
45+
this is what will provide all the workshop definition (but not the workshop content).
46+
47+
Workshop definition means things like title and description of the workshop, location of the content files,
48+
workshop capabilities that should be present and how (terminal, editor, console/dashboard, docker, registry, vcluster, ...) as well as workshop sandbox sizing.
49+
50+
Don't worry too much about all of these as we will dig deeper in later posts. The thing that matters most is
51+
that this is the file that will be applied into your Kubernetes cluster and that will install the workshop
52+
on the cluster (although it will not instantiate it).
53+
54+
Then, we can see that there's a folder named `workshop`, where we will have the `content`, written in Markdown,
55+
following [Hugo](https://gohugo.io) conventions.
56+
57+
We can now apply this file to the Educates cluster using Kubectl, and see what happens.
58+
59+
```
60+
kubectl apply -f resources/workshop.yaml
61+
```
62+
63+
When you list the workshop resources in the cluster, you will see nothing interesting:
64+
65+
````
66+
$ kubectl get workshop
67+
NAME URL
68+
my-workshop
69+
```
70+
71+
And a more descriptive description will not give you much info still:
72+
```
73+
$ kubectl describe workshop my-workshop
74+
Name: my-workshop
75+
Namespace:
76+
Labels: <none>
77+
Annotations: <none>
78+
API Version: training.educates.dev/v1beta1
79+
Kind: Workshop
80+
Metadata:
81+
Creation Timestamp: 2024-12-04T18:09:45Z
82+
Generation: 1
83+
Resource Version: 3505
84+
UID: e9062380-8964-4041-9955-c74322729390
85+
Spec:
86+
Description: First workshop using Educates
87+
Publish:
88+
Image: $(image_repository)/my-workshop-files:$(workshop_version)
89+
Session:
90+
Applications:
91+
Console:
92+
Enabled: false
93+
Docker:
94+
Enabled: false
95+
Editor:
96+
Enabled: true
97+
Registry:
98+
Enabled: false
99+
Terminal:
100+
Enabled: true
101+
Layout: split
102+
Vcluster:
103+
Enabled: false
104+
Namespaces:
105+
Budget: medium
106+
Title: My first workshop
107+
Workshop:
108+
Files:
109+
Image:
110+
URL: $(image_repository)/my-workshop-files:$(workshop_version)
111+
Include Paths:
112+
/workshop/**
113+
/exercises/**
114+
/README.md
115+
Path: .
116+
Events: <none>
117+
```
118+
119+
This is because of 2 reasons:
120+
121+
1. The workshop is just a static definition. There's another resource named `TrainingPortal` which is
122+
actually the one that will instantiate the workshops and make them available for access.
123+
1. The workshop contents are nowhere but your local folder. The cluster can't access them in any way.
124+
125+
Let's solve both problems at once by using Educates CLI capabilities.
126+
127+
Let's first publish the workshop. In the workshop definition file we will find a section that describes
128+
where Kubernetes could find the workshop files
129+
130+
```
131+
workshop:
132+
files:
133+
- image:
134+
url: "$(image_repository)/my-workshop-files:$(workshop_version)"
135+
includePaths:
136+
- /workshop/**
137+
- /exercises/**
138+
- /README.md
139+
```
140+
141+
This snippet instructs Kubernetes that the image at the provided `url` is the one that contains the
142+
workshop contents, and that it should only look at the `workshop` and `exercises` folders.
143+
144+
Educates, when is instantiating a Workshop will pull down this OCI image and extract it and use the content
145+
in those folders as the workshop content. Fine, but that URL looks weird.
146+
147+
This is where the publish section of the workshop definition comes into play.
148+
149+
```
150+
publish:
151+
image: "$(image_repository)/my-workshop-files:$(workshop_version)"
152+
```
153+
154+
Educates, when publishing a workshop, will package your local folder up into an OCI image and will push it
155+
into the specified image registry's coordinates.
156+
157+
Fine! But that URL still looks like something not in the Internet. True!
158+
159+
By default, when developing locally, Educates CLI is configured to replace `$(image_repository)` and `$(workshop_version)` and use the local cluster's coordinates, which it knows very well.
160+
161+
So, let's go ahead and publish this workshop and see what happens:
162+
163+
```
164+
$ educates publish-workshop
165+
Processing workshop with name "my-workshop".
166+
Publishing workshop files to "localhost:5001/my-workshop-files:latest".
167+
dir: .
168+
file: .gitignore
169+
file: README.md
170+
dir: resources
171+
file: resources/workshop.yaml
172+
dir: workshop
173+
file: workshop/config.yaml
174+
dir: workshop/content
175+
file: workshop/content/00-workshop-overview.md
176+
file: workshop/content/01-workshop-instructions.md
177+
file: workshop/content/99-workshop-summary.md
178+
Pushed 'localhost:5001/my-workshop-files@sha256:4b6b6e165357f691460c05f6637fa35df649a572ca6ae3b2154bae6e8c4bd2c7'
179+
```
180+
181+
You wll notice that the image name at the second line of the output contains a well formed
182+
OCI registry's image coordinates. The host is `localhost:5001` and the version is latest.
183+
184+
Educates Training Platform's local cluster provides a local Docker registry, wired up into the cluster,
185+
that is accessible from the outside of the cluster at `localhost:5001` but internally is located at
186+
`registry.default.svc.cluster.local` as a Kubernetes service.
187+
188+
Let's now push the workshop into the cluster. But this time, we will use the Educates CLI.
189+
190+
```
191+
$ educates deploy-workshop
192+
Loaded workshop "educates-cli--my-workshop-805a574".
193+
Creating new training portal "educates-cli".
194+
Workshop added to training portal.
195+
```
196+
197+
Here, even though the output is not very verbose, it it very interesting. First, a workshop has been
198+
created with a hashed name `educates-cli--my-workshop-805a574`. This hash is made to prevent collisions
199+
but at the same time to provide reproducibility. The former is achieved by giving a longer name that contains
200+
the name of the TrainingPortal, the workshop and a hash on the configuration file. The latter, given that
201+
the hash is done on the configuration, if you alter the configuration, a new workshop instance will be created,
202+
otherwise, if you just update the content, the same workshop definition will still be applicable.
203+
204+
The second line states that a new Training Portal has been created. Let's verify that:
205+
206+
```
207+
$ kubectl get trainingportal
208+
NAME URL PORTALPASSWORD ADMINUSERNAME ADMINPASSWORD STATUS MESSAGE
209+
educates-cli http://educates-cli-ui.kind.mac16.tanzu-devs.com %qKPg:jg5zB9 educates TPq5pMNr7iDXc8Fg0n2BvZtGK4EVskaI Running
210+
```
211+
212+
In this output, there's now a single trainingportal with name `educates-cli` and there's a URL now and a portal password. When you click on this URL, you should now see an Educates screen asking you to provide a password.
213+
This is the portal password. Once you input the portal password you have there, you should see the main Training Portal screen with the list of Workshops for that Portal. In this case, that will be your `My workshop`.
214+
215+

0 commit comments

Comments
 (0)