Workshop Environment

The WorkshopEnvironment custom resource defines a workshop environment.

The raw custom resource definition for the WorkshopEnvironment custom resource can be viewed at:

kubectl get crd/workshopenvironments.training.educates.dev -o yaml

Note that as deployment and use of workshops is now all typically managed via a training portal, there is no need to interact with internal custom resources directly such as WorkshopEnvironment, WorkshopSession and WorkshopRequest directly. In other words this resource has become an internal implementation detail of the larger system. For that reason the documentation here may not be completely up to date.

Specifying the workshop definition

The creation of a workshop environment is performed as a separate step to loading the workshop definition. This is to allow multiple distinct workshop environments using the same workshop definition to be created if necessary.

To specify which workshop definition is to be used for a workshop environment, set the workshop.name field of the specification for the workshop environment.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample

The name of the workshop environment specified in the metadata of the workshop environment does not need to be the same, and has to be different if creating multiple workshop environments from the same workshop definition.

When the workshop environment is created, the namespace created for the workshop environment will use the name of the workshop environment specified in the metadata. This name will also be used in the unique names of each workshop instance created under the workshop environment.

Overriding environment variables

A workshop definition may specify a list of environment variables that need to be set for all workshop instances. If you need to override an environment variable specified in the workshop definition, or one which is defined in the container image, you can supply a list of environment variables as session.env.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  session:
    env:
    - name: REPOSITORY_URL
      value: https://github.com/vmware-tanzu-labs/lab-markdown-sample

You might use this to set the location of a backend service, such as an image registry, to be used by the workshop.

Values of fields in the list of resource objects can reference a number of pre-defined parameters. The available parameters are:

  • session_id - A unique ID for the workshop instance within the workshop environment.

  • session_namespace - The namespace created for and bound to the workshop instance. This is the namespace unique to the session and where a workshop can create their own resources.

  • environment_name - The name of the workshop environment. For now this is the same as the name of the namespace for the workshop environment. Don’t rely on them being the same, and use the most appropriate to cope with any future change.

  • workshop_namespace - The namespace for the workshop environment. This is the namespace where all deployments of the workshop instances are created, and where the service account that the workshop instance runs as exists.

  • service_account - The name of the service account the workshop instance runs as, and which has access to the namespace created for that workshop instance.

  • ingress_domain - The host domain under which hostnames can be created when creating ingress routes.

  • ingress_protocol - The protocol (http/https) that is used for ingress routes which are created for workshops.

  • services_password - A unique random password value for use with arbitrary services deployed with a workshop.

  • ssh_private_key - The private part of a unique SSH key pair generated for the workshop session.

  • ssh_public_key - The public part of a unique SSH key pair generated for the workshop session.

  • ssh_keys_secret - The name of the Kubernetes secret in the workshop namespace holding the SSH key pair for the workshop session.

  • platform_arch - The CPU architecture the workshop container is running on, amd64 or arm64.

The syntax for referencing one of the parameters is $(parameter_name).

Overriding the ingress domain

In order to be able to access a workshop instance using a public URL, you will need to specify an ingress domain. If an ingress domain isn’t specified, the default ingress domain that the Educates operator has been configured with will be used.

When setting a custom domain, DNS must have been configured with a wildcard domain to forward all requests for sub domains of the custom domain, to the ingress router of the Kubernetes cluster.

To provide the ingress domain, you can set the session.ingress.domain field.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  session:
    ingress:
      domain: training.educates.dev

If overriding the domain, by default, the workshop session will be exposed using a HTTP connection. If you require a secure HTTPS connection, you will need to have access to a wildcard SSL certificate for the domain. A secret of type tls should be created for the certificate in the Educates operator namespace. The name of that secret should then be set in the session.ingress.secret field.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  session:
    ingress:
      domain: training.educates.dev
      secret: training.educates.dev-tls

If HTTPS connections are being terminated using an external load balancer and not by specificying a secret for ingresses managed by the Kubernetes ingress controller, with traffic then routed into the Kubernetes cluster as HTTP connections, you can override the ingress protocol without specifying an ingress secret by setting the session.ingress.protocol field.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  session:
    ingress:
      domain: training.educates.dev
      protocol: https

If you need to override or set the ingress class, which dictates which ingress router is used when more than one option is available, you can add session.ingress.class.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  session:
    ingress:
      domain: training.educates.dev
      secret: training.educates.dev-tls
      class: nginx

Controlling access to the workshop

By default, the ability to request a workshop using the WorkshopRequest custom resource is disabled and so must be enabled for a workshop environment by setting request.enabled to true.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  request:
    enabled: true

With this enabled, anyone able to create a WorkshopRequest custom resource could request the creation of a workshop instance for the workshop environment.

To further control who can request a workshop instance in the workshop environment, you can first set an access token, which a user would need to know and supply with the workshop request. This can be done by setting the request.token field.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  request:
    enabled: true
    token: lab-markdown-sample

In this example the same name as the workshop environment is used, which is probably not a good practice. Use a random value instead. The token value can be multiline if desired.

As a second measure of control, you can specify what namespaces the WorkshopRequest needs to be created in to be successful. This means a user would need to have the specific ability to create WorkshopRequest resources in one of those namespaces.

The list of namespaces from which workshop requests for the workshop environment is allowed can be specified by setting request.namespaces.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  request:
    enabled: true
    token: lab-markdown-sample
    namespaces:
    - default

If you want to add the workshop namespace in the list, rather than list the literal name, you can reference a predefined parameter specifying the workshop namespace by including $(workshop_namespace).

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  request:
    enabled: true
    token: lab-markdown-sample
    namespaces:
    - $(workshop_namespace)

Overriding the login credentials

When requesting a workshop using WorkshopRequest, a login prompt for the workshop instance will be presented to a user when the URL for the workshop instance is accessed. By default the username they need to use will be educates. The password will be a random value which they need to query from the WorkshopRequest status after the custom resource has been created.

If you want to override the username, you can specify the session.username field. If you want to set the same fixed password for all workshop instances, you can specify the session.password field.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  session:
    username: workshop
    password: lab-markdown-sample

Additional workshop resources

The workshop definition defined by the Workshop custom resource already declares a set of resources to be created with the workshop environment. This could be used when you have shared service applications needed by the workshop, such as an image registry, or a Git repository server.

If you need to deploy additional applications related to a specific workshop environment, you can declare them by adding them into the environment.objects field of the WorkshopEnvironment custom resource. You might use this deploy a web application used by attendees of a workshop to access their workshop instance.

For namespaced resources, it is not necessary to specify the namespace field of the resource metadata. When the namespace field is not present the resource will automatically be created within the workshop namespace for that workshop environment.

When resources are created, owner references are added making the WorkshopEnvironment custom resource corresponding to the workshop environment the owner. This means that when the workshop environment is deleted, any resources will be automatically deleted.

Values of fields in the list of resource objects can reference a number of pre-defined parameters. The available parameters are:

  • workshop_name - The name of the workshop. This is the name of the Workshop definition the workshop environment was created against.

  • environment_name - The name of the workshop environment. For now this is the same as the name of the namespace for the workshop environment. Don’t rely on them being the same, and use the most appropriate to cope with any future change.

  • environment_token - The value of the token which needs to be used in workshop requests against the workshop environment.

  • workshop_namespace - The namespace for the workshop environment. This is the namespace where all deployments of the workshop instances, and their service accounts, are created. It is the same namespace that shared workshop resources are created.

  • service_account - The name of a service account that can be used when creating deployments in the workshop namespace.

  • ingress_domain - The host domain under which hostnames can be created when creating ingress routes.

  • ingress_protocol - The protocol (http/https) that is used for ingress routes which are created for workshops.

  • ingress_secret - The name of the ingress secret stored in the workshop namespace when secure ingress is being used.

If you want to create additional namespaces associated with the workshop environment, embed a reference to $(workshop_namespace) in the name of the additional namespaces, with an appropriate suffix. Be mindful that the suffix doesn’t overlap with the range of session IDs for workshop instances.

When creating deployments in the workshop namespace, set the serviceAccountName of the Deployment resouce to $(service_account). This will ensure the deployment makes use of a special pod security policy set up by Educates. If this isn’t used and the cluster imposes a more strict default pod security policy, your deployment may not work, especially if any image expects to run as root.

Creation of workshop instances

Once a workshop environment has been created you can create the workshop instances. A workshop instance can be requested using the WorkshopRequest custom resource. This can be done as a separate step, or you can use the trick of adding them as resources under environment.objects.

apiVersion: training.educates.dev/v1beta1
kind: WorkshopEnvironment
metadata:
  name: lab-markdown-sample
spec:
  workshop:
    name: lab-markdown-sample
  request:
    token: lab-markdown-sample
    namespaces:
    - $(workshop_namespace)
  session:
    username: educates
    password: lab-markdown-sample
  environment:
    objects:
    - apiVersion: training.educates.dev/v1beta1
      kind: WorkshopRequest
      metadata:
        name: user1
      spec:
        environment:
          name: $(environment_name)
          token: $(environment_token)
    - apiVersion: training.educates.dev/v1beta1
      kind: WorkshopRequest
      metadata:
        name: user2
      spec:
        environment:
          name: $(environment_name)
          token: $(environment_token)

Using this method, the workshop environment will be automatically populated with workshop instances. You will need to query the workshop requests from the workshop namespace to determine the URLs for accessing each, and the password if you didn’t set one and a random password was assigned.

If you needed more control over how the workshop instances were created using this method, you could use the WorkshopSession custom resource instead.