Skip to content

Commit

Permalink
Add ExerciseSet object (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
dergeberl authored May 19, 2021
1 parent 22fdec1 commit 4dba0b5
Show file tree
Hide file tree
Showing 29 changed files with 1,917 additions and 354 deletions.
69 changes: 32 additions & 37 deletions .github/kind-test-taskdefinitions.yaml
Original file line number Diff line number Diff line change
@@ -1,44 +1,39 @@
---
apiVersion: kubeteach.geberl.io/v1alpha1
kind: TaskDefinition
kind: ExerciseSet
metadata:
name: task01
spec:
taskSpec:
title: "task01"
description: "task01 - should be successful"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "task01"
---
apiVersion: kubeteach.geberl.io/v1alpha1
kind: TaskDefinition
metadata:
name: task02
spec:
taskSpec:
title: "task02"
description: "task02 - should be active"
requiredTaskName: "task01"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "task02"
---
apiVersion: kubeteach.geberl.io/v1alpha1
kind: TaskDefinition
metadata:
name: task03
name: set
spec:
taskSpec:
title: "task03"
description: "task03 - should be pending"
requiredTaskName: "task02"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "task03"
taskDefinitions:
- name: task01
taskDefinitionSpec:
taskSpec:
title: "task01"
description: "task01 - should be successful"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "task01"
- name: task02
taskDefinitionSpec:
taskSpec:
title: "task02"
description: "task02 - should be active"
requiredTaskName: "task01"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "task02"
- name: task03
taskDefinitionSpec:
taskSpec:
title: "task03"
description: "task03 - should be pending"
requiredTaskName: "task02"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "task03"
---
apiVersion: v1
kind: Namespace
Expand Down
2 changes: 1 addition & 1 deletion .golangci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ linters:
- gocritic
- gofmt
- goimports
- golint
- revive
- gomnd
- goprintffuncname
- gosec
Expand Down
9 changes: 9 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,13 @@ resources:
kind: TaskDefinition
path: github.com/dergeberl/kubeteach/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: geberl.io
group: kubeteach
kind: ExerciseSet
path: github.com/dergeberl/kubeteach/api/v1alpha1
version: v1alpha1
version: "3"
91 changes: 81 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,15 @@ You need also `kubectl` to interact with your cluster. Checkout the [install kub

### Installation

To install kubeteach to your cluster, you have to deploy the operator itself by applying the deployment file in the `deployment` folder.
To install kubeteach to your cluster, you have to deploy the operator itself by applying the latest deployment file.
```bash
git clone dergeberl/kubeteach
cd kubeteach
kubectl apply -f deployment/
kubectl apply -f https://github.com/dergeberl/kubeteach/releases/latest/download/deployment.yaml
```

Now you can deploy a set of exercises to your cluster.
Now you can deploy a set of exercises to your cluster.

```bash
kubectl apply -f exercises/set1/
kubectl apply -f https://github.com/dergeberl/kubeteach/releases/latest/download/exerciseset1.yaml
```

### Usage
Expand Down Expand Up @@ -113,9 +111,82 @@ If you need help you can take a look into the solution folder of the exercise se

## How it works / How to write own exercises

In the `exercise` folder is a set of `taskdefinitions` which describe a `task` and conditions to check if the task is successful.
### ExerciseSet (optional)

### taskCondition
This is optional you can create directly `TaskDefinition`.

An `ExerciseSet` contains one or multiple `TaskDefinitions` to group them together and get some metadata from this `TaskDefinition`'s.

Each `spec.taskDefinitions` consists of a `name` (name of the `TaskDefinition` object) and a `taskDefinitionSpec` (spec of the `TaskDefinition`, see below).

#### Example

```yaml
apiVersion: kubeteach.geberl.io/v1alpha1
kind: ExerciseSet
metadata:
name: set1
spec:
taskDefinitions:
- name: task01
taskDefinitionSpec:
taskSpec:
title: "Create namespace"
description: "Create a new namespace with the name kubeteach"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "kubeteach"
points: 5
- name: task02
taskDefinitionSpec:
taskSpec:
title: "Create namespace"
description: "Create a new namespace with the name kubeteach2"
taskCondition:
- apiVersion: v1
kind: Namespace
name: "kubeteach2"
points: 5
```
#### Status
The `ExerciseSet` status contains some metadata information of the tasks.

```yaml
...
status:
numberOfActiveTasks: 2
numberOfPendingTasks: 11
numberOfSuccessfulTasks: 0
numberOfTasks: 13
numberOfTasksWithoutPoints: 0
numberOfUnknownTasks: 0
pointsAchieved: 0
pointsTotal: 65
...
```

### TaskDefinition

A `TaskDefinition` describes a `Task` and conditions to check if the task is successful.

#### taskSpec

The `taskSpec` will be copied to the `Task` and is the object which is used for solving tasks. It should contain all information which are needed to solve the `Task`.

The following fields are available:
- `title` - title of the task
- `description` - description of the task which is shown by `kubectl get tasks`
- `longDescription` (optional) - longer description of the task which is shown by `kubectl describe tasks`
- `helpURL` (optional) - an url to more information about the topic in the task

#### points

`points` is an optional field which is only used if the `TaskDefinition` is created by an `ExerciseSet` to sum all points inside the `ExerciseSet`-status.

#### taskCondition

To check if the task is successful there is a list of `taskCondition`.
Each `taskCondition` describes an object (apiVersion, kind and name) and contains a list of `resourceCondition` (see below).
Expand All @@ -125,7 +196,7 @@ To check if an object doesn't exist you can use `spec.taskConditions.notExists`

To depend on another task you can link a task as required with `spac.requiredTaskName`. This task will be in pending until the required task is successful. Be careful there is no check if the tasks can ever become active or are stuck in pending forever.

### resourceCondition
#### resourceCondition

Each `resourceCondition` contains a `field` which should be checked, an `operator` (see table below) and a `value`.

Expand All @@ -143,7 +214,7 @@ The `field` is a json path to find the field witch should be checked. The json p

If there are multiple `taskCondition` and `resourceCondition` then **all** must be successful to complete the task.

### Example
#### Example

A simple example to check if a namespace is created:

Expand Down
85 changes: 85 additions & 0 deletions api/v1alpha1/cases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,3 +362,88 @@ var taskDefinitionCases = []testCases{
err: Not(BeNil()),
},
}

var exerciseSetCases = []testCases{
{
obj: &ExerciseSet{
ObjectMeta: metav1.ObjectMeta{Name: "exerciseset-valid1", Namespace: "default"},
Spec: ExerciseSetSpec{
TaskDefinitions: []ExerciseSetSpecTaskDefinitions{
{
Name: "test1",
TaskDefinitionSpec: TaskDefinitionSpec{
TaskSpec: TaskSpec{
Title: "task",
Description: "task",
},
TaskConditions: []TaskCondition{
{
APIVersion: "v1",
Kind: "Namespace",
Name: "default",
ResourceCondition: nil,
},
},
},
},
},
},
},
err: BeNil(),
}, {
obj: &ExerciseSet{
ObjectMeta: metav1.ObjectMeta{Name: "exerciseset-invalid1", Namespace: "default"},
Spec: ExerciseSetSpec{
TaskDefinitions: []ExerciseSetSpecTaskDefinitions{
{
Name: "test1",
TaskDefinitionSpec: TaskDefinitionSpec{
TaskSpec: TaskSpec{},
TaskConditions: []TaskCondition{},
RequiredTaskName: nil,
Points: 0,
},
},
},
},
},
err: Not(BeNil()),
}, {
obj: &ExerciseSet{
ObjectMeta: metav1.ObjectMeta{Name: "exerciseset-invalid2", Namespace: "default"},
Spec: ExerciseSetSpec{
TaskDefinitions: []ExerciseSetSpecTaskDefinitions{
{
TaskDefinitionSpec: TaskDefinitionSpec{
TaskSpec: TaskSpec{
Title: "task",
Description: "task",
},
TaskConditions: []TaskCondition{
{
APIVersion: "v1",
Kind: "Namespace",
Name: "default",
},
},
},
},
},
},
},
err: Not(BeNil()),
}, {
obj: &ExerciseSet{
ObjectMeta: metav1.ObjectMeta{Name: "exerciseset-invalid3", Namespace: "default"},
Spec: ExerciseSetSpec{
TaskDefinitions: []ExerciseSetSpecTaskDefinitions{
{
Name: "test",
TaskDefinitionSpec: TaskDefinitionSpec{},
},
},
},
},
err: Not(BeNil()),
},
}
Loading

0 comments on commit 4dba0b5

Please sign in to comment.