{"id":24592345,"url":"https://github.com/statcan/zone-kubeflow-containers","last_synced_at":"2026-03-06T20:06:48.746Z","repository":{"id":273936618,"uuid":"921349603","full_name":"StatCan/zone-kubeflow-containers","owner":"StatCan","description":"Containers built to be used with Kubeflow for Data Science","archived":false,"fork":false,"pushed_at":"2026-03-02T15:34:15.000Z","size":4907,"stargazers_count":2,"open_issues_count":15,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-03-02T15:37:20.725Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StatCan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-01-23T19:39:44.000Z","updated_at":"2026-03-02T11:37:24.000Z","dependencies_parsed_at":"2025-02-17T16:26:46.005Z","dependency_job_id":"433fbd2b-1bad-49ef-93ae-8245cb55ea60","html_url":"https://github.com/StatCan/zone-kubeflow-containers","commit_stats":null,"previous_names":["statcan/zone-kubeflow-containers"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/StatCan/zone-kubeflow-containers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fzone-kubeflow-containers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fzone-kubeflow-containers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fzone-kubeflow-containers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fzone-kubeflow-containers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StatCan","download_url":"https://codeload.github.com/StatCan/zone-kubeflow-containers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StatCan%2Fzone-kubeflow-containers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30195589,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-06T19:07:06.838Z","status":"ssl_error","status_checked_at":"2026-03-06T18:57:34.882Z","response_time":250,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-24T10:14:14.944Z","updated_at":"2026-03-06T20:06:48.718Z","avatar_url":"https://github.com/StatCan.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zone-kubeflow-containers\n\nContainer images to be used with [The Zone](https://zone.statcan.ca).\nUser documentation can be found at https://zone.pages.cloud.statcan.ca/docs/en/\n\n## Table of Contents\n\u003c!-- toc --\u003e\n- [Introduction](#introduction)\n- [List of maintained images in this github repository](#list-of-maintained-images-in-this-github-repository)\n- [Usage](#usage)\n  - [Building and Tagging Docker Images](#building-and-tagging-docker-images)\n  - [Pulling and Pushing Docker Images](#pulling-and-pushing-docker-images)\n  - [Testing Images](#testing-images)\n    - [Running and Connecting to Images Locally/Interactively](#running-and-connecting-to-images-locallyinteractively)\n    - [Automated Testing](#automated-testing)\n- [General Development Workflow](#general-development-workflow)\n  - [Overview of Images](#overview-of-images)\n  - [Running A Zone Container Locally](#running-a-zone-container-locally)\n  - [Testing locally](#testing-locally)\n  - [Testing On-Platform](#testing-on-platform)\n  - [Adding new software](#adding-new-software)\n  - [Adding new Images](#adding-new-images)\n  - [Modifying and Testing CI](#modifying-and-testing-ci)\n- [Beta Process](#beta-process)\n- [Other Development Notes](#other-development-notes)\n  - [Github CI](#github-ci)\n  - [The `v2` and `latest` tags for the master branch](#the-v2-and-latest-tags-for-the-master-branch)\n  - [Set User File Permissions](#set-user-file-permissions)\n  - [Troubleshooting](#troubleshooting)\n- [Structure](#structure)\n\u003c!-- tocstop --\u003e\n\n## Introduction\n\nOur Container images are based on the community driven [jupyter/docker-stacks](https://github.com/jupyter/docker-stacks).\nWe chose those images because they are continuously updated and install the most common utilities.\nThis enables us to focus only on the additional toolsets that we require to enable our data scientists.\nThese customized images are maintained by the Zone team and are the default images available on The Zone.\n\n## Overview of Images\n\nEach directory in the images folder makes up one stage of the build process.\nThey each contain the Dockerfile that directs the build, and all related files.\n\nThe relationship between the stages and the final product is as shown below.\n```mermaid\ngraph TD\n  upstream_nb[\"(upstream) datascience-notebook\"]\n  upstream_nb --\u003e base\n  base --\u003e mid\n  mid --\u003e sas_kernel\n  upstream_sas[\"(upstream) sas4c\"] --\u003e |copy|sas_kernel\n  sas_kernel --\u003e jupyterlab[\"jupyterlab (jupyterlab-cpu)\"]\n  sas_kernel --\u003e sas\n```\n\n### Base Images\n\nThese images are chained together to perform the multi-staged build for our final images\n\nImage | Notes\n--- | ---\n[base](./images/base) | Base Image pulling from docker-stacks\n[mid](./images/mid) | Installs various tools on top of the base image\n[sas-kernel](./images/sas_kernel) | Installs the SAS kernel on our mid image\n\n### Zone Images\n\nThese are the final images from our build process and are intended to be used on Kubeflow Notebooks\n\nImage | Notes | Installations\n--- | --- | ---\n[jupyterlab-cpu](./images/jupyterlab) | The base experience. A jupyterlab notebook with various | Jupyter, VsCode, R, Python, Julia, Sas kernel\n[sas](./images/sas) | Similar to our jupyterlab-cpu image, except with SAS Studios | Sas Studios\n\n## Usage\n\n### Building Images\n\nWe have setup [Docker Bake](https://docs.docker.com/build/bake/) to help with building our images. Docker Bake lets us define our build configuration for our images through a file instead of CLI instructions.\n\nTo build an image, you can use either `make bake/IMAGE` or `docker buildx bake IMAGE`. `docker build` commands can still work if desired.\n\n`make bake` accepts overrides for BASE_IMAGE, REPO and TAGS to adjust these values for the build.\n\nTo review any parameters for the image builds, you can review and edit the [docker-bake.hcl](./docker-bake.hcl) file. \nThis file is currently setup for local development. We use parameter overrides for our github workflows to adjust the docker bake file for our CI/CD process.\n\n**Note:** Our workflows save all our images in our Azure Container Registry. To pull and push to our ACR locally, \nyou will first have to login using `az acr login -n k8scc01covidacr`\n\n**Note:** `make push` by default does `docker push --all-tags` in order to push the SHA, SHORT_SHA, etc., tags.  \n\n### Testing Images\n\n#### Running and Connecting to Images Locally/Interactively\n\nTo test an image interactively, use `make dev/IMAGENAME`.\nThis calls `docker run` on a built image,\nautomatically forwarding ports to your local machine and providing a link to connect to.\nOnce the docker container is running, it will serve a localhost url to connect to the notebook.\n\n#### Automated Testing\n\nAutomated tests are included for the generated Docker images using `pytest`.\nThis testing suite is modified from the [docker-stacks](https://github.com/jupyter/docker-stacks) test suite.\nImage testing is invoked through `make test/IMAGENAME`\n(with optional `REPO` and `TAG` arguments like `make build`).\n\nTesting of a given image consists of general and image-specific tests:\n\n```\n└── tests\n    ├── general                             # General tests applied to all images\n    │   └── some_general_test.py\n    └── jupyterlab-cpu                      # Test applied to a specific image\n        └── some_jupyterlab-cpu-specific_test.py\n```\n\nWhere `tests/general` tests are applied to all images,\nand `tests/IMAGENAME` are applied only to a specific image.\nPytest will start the image locally and then run the provided tests to determine if Jupyterlab is running, python packages are working properly, etc.\nTests are formatted using typical pytest formats\n(python files with `def test_SOMETHING()` functions).\n`conftest.py` defines some standard scaffolding for image management, etc.\n\n---\n\n## General Development Workflow\n\n### Running A Zone Container Locally\n\n1. Clone the repository with `git clone https://github.com/StatCan/zone-kubeflow-containers`.\n2. Run `make install-python-dev-venv` to build a development Python virtual environment.\n2.5 Add back from statements in Dockerfiles.\n3. Build your image using `make bake/IMAGENAME`,\ne.g. run `make bake/base`.\n4. Test your image using automated tests through `make test/IMAGENAME`,\ne.g. run `make test/sas`.\nRemember that tests are designed for the final stage of a build.\n5. View your images with `docker images`.\nYou should see a table printed in the console with your images.\nFor example you may see:\n\n```\nusername@hostname:~$ docker images\nREPOSITORY                                  TAG        IMAGE ID       CREATED          SIZE\nk8scc01covidacr.azurecr.io/jupyterlab-cpu   v2         13f8dc0e4f7a   26 minutes ago   14.6GB\nk8scc01covidacr.azurecr.io/sas              v2         2b9acb795079   19 hours ago     15.5GB\n```\n\n7. Run your image with `docker run -p 8888:8888 REPO/IMAGENAME:TAG`, e.g. `docker run -p 8888:8888 k8scc01covidacr.azurecr.io/sas:v2`.\n8. Open [http://localhost:8888](http://localhost:8888) or `\u003cip-address-of-server\u003e:8888`.\n\n### Testing locally\n\n1. Clone the repo\n2. Edit an image via the [image stages](/images) that are used to create it.\n3. Build your edited stages and any dependencies using `make bake/IMAGENAME`\n    * (optional) Run `docker pull REPO/IMAGENAME:TAG` to pull an existing version of the image you are working on \n    (this could be useful as a build cache to reduce development time below)\n    * (optional) If the BASE_IMAGE is not build locally for the image stage you want to build, you will have to either run `make bake/BASE_IMAGE` to build it locally, \n    or you will have to pull the image.\n4. Test your image:\n    * using automated tests through `make test/IMAGENAME`\n    * manually by `docker run -it -p 8888:8888 REPO/IMAGENAME:TAG`,\n     then opening it in [http://localhost:8888](http://localhost:8888)\n\n### Testing On-Platform\n\nGitHub Actions CI is enabled to do building, scanning, automated testing, pushing of our images to ACR.\nThe workflows will trigger on the following:\n\n- any push to master or beta\n- any push to an open PR that edits files in `.github/workflows/` or `/images/`\n\nThis allows for easy scanning and automated testing for images.\n\nAfter the workflow is complete,\nthe images will be available on artifactory.cloud.statcan.ca/das-aaw-docker.\nYou can access these images on https://zone.statcan.ca using any of the following:\n\n- artifactory.cloud.statcan.ca/das-aaw-docker/IMAGENAME:BRANCH_NAME\n- artifactory.cloud.statcan.ca/das-aaw-docker/IMAGENAME:SHA\n- artifactory.cloud.statcan.ca/das-aaw-docker/IMAGENAME:SHORT_SHA\n\nPushes to master will also have the following tags:\n\n- artifactory.cloud.statcan.ca/das-aaw-docker/IMAGENAME:latest\n- artifactory.cloud.statcan.ca/das-aaw-docker/IMAGENAME:v2\n\n### Adding new software\n\nSoftware needs to be added by modifying the relevant image stage,\nthen following the normal build instructions starting with the Generate Dockerfiles step.\n\nBe selective with software installation as image sizes are already quite big (16Gb plus),\nand increasing that size would negatively impact the time it takes up for a workspace server to come up\n(as well as first time image pulls to a node).\nIn such cases it may be more relevant to make an image under [aaw-contrib-containers](https://github.com/StatCan/aaw-contrib-containers) as mentioned earlier.\n\n### Adding new Images\n\n1. Identify where the new stage will be placed in the build order\n2. Create a new subdirectory in the `/images/` directory for the stage\n3. Add a new target to the `docker-bake.hcl` file for the new stage.\n    ```\n    # general format for a bake target\n    target \"stage-name\" {\n      args = {\n        BASE_IMAGE=\"BASE_IMAGE\"         # ARGS values from the dockerfile\n      }\n      context = \"./images/stage-name\"   # points to the location of the dockerfile\n      tags = [\"stage-name\"]             # name given to the built docker image\n    }\n    ```\n4. Add a new job to the `./github/workflows/docker.yaml` for the new stage.\n\n    ```yaml\n    # yaml to create an image\n    stage-name:                                                         # The name of the stage, will be shown in the CICD workflow\n      needs: [vars, parent]                                             # All stages need vars, any stages with a parent must link their direct parent\n      uses: ./.github/workflows/docker-steps.yaml\n      with:\n        image: \"stage-name\"                                             # The name of the current stage/image\n        directory: \"directory-name\"                                     # The name of the directory in the /images/ folder. /images/base would be \"base\"\n        base-image: \"quay.io/jupyter/datascience-notebook:2024-06-17\"   # used if the stage is built from an upsteam image. Omit if stage has a local parent\n        parent-image: \"parent\"                                          # The name of the parent stage/image. Omit if stage uses an upsteam image\n        parent-image-is-diff: \"${{ needs.parent.outputs.is-diff }}\"     # Checks if the parent image had changes. Omit if stage uses an upsteam image\n        # The following values are static between differnt stages\n        registry-name: \"${{ needs.vars.outputs.REGISTRY_NAME }}\"\n        branch-name: \"${{ needs.vars.outputs.branch-name }}\"\n      secrets:\n        REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}\n        REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}\n    ```\n\n5. If this stage was inserted between two existing stages,\nupdate the parent values of any children of this stage\n6. If this stage creates an image that will be deployed to users.\nA job must be added to test the image in `./github/workflows/docker.yaml`,\nand the image name must be added to the matrix in `./github/workflows/docker-nightly.yaml`\n\n    ```yaml\n    # yaml to create a test\n    imagename-test:                                       # The name of the test job, usually  imagename-test\n      needs: [vars, imagename]                            # Must contain vars and the image that will be tested\n      uses: ./.github/workflows/docker-pull-test.yaml\n      with:\n        image: \"imagename\"                                # The name of the image that will be tested\n        # The following values are static between differnt tests\n        registry-name: \"${{ needs.vars.outputs.REGISTRY_NAME }}\"\n        tag: \"${{ needs.vars.outputs.branch-name }}\"\n      secrets:\n        REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}\n        REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}\n        CVE_ALLOWLIST: ${{ secrets.CVE_ALLOWLIST}}\n    ```\n\n7. Update the documentation for the new stage.\nThis is generally updating `images-stages.png` and `image-stages.drawio` in the `docs/images` folder using draw.io.\n\n### Custom scripts\n\nTo manage our custom scripts that we want to execute after a container starts up, we use the [s6-overlay](https://github.com/just-containers/s6-overlay). Kubeflow upstream also uses this tool with their [example notebook servers](https://github.com/kubeflow/kubeflow/blob/master/components/example-notebook-servers/README.md#configure-s6-overlay)\n\n\nScripts that need to run during the startup of the container can be placed in `/etc/cont-init.d/`, and are executed in ascending alphanumeric order.\n\nScripts like our [start-custom](./images/mid/s6/cont-init.d/02-start-custom) use the with-contenv helper so that environment variables (passed to container) are available in the script.\n\nExtra services to be monitored by s6-overlay should be placed in their own folder under `/etc/services.d/` containing a script called `run` and optionally a finishing script `finish`.\n\nAn example of a long-running service can be found in our [main run script](./images/mid/s6/services.d/jupyter/run) which is used to start JupyterLab itself.\n\n#### Note on setting environment variables in startup scripts\n\nWhen using both a startup script and a service script, environment variables declared in the startup script (using `export VAR=value` for example) will not be available in the service script.\n\nTo circumvent this limitation, if you need to declare new environment variables from a custom script, you can first create a custom environment location, like `/run/s6-env`. Then, you can store your new environment variables in that new location, using for example `echo ${TEST_ENV_VAR} \u003e /run/s6-env/TEST_ENV_VAR`. \nThen, when executing the long-running service, you can use `s6-envdir /run/s6-env` to point to your custom environment location in the `exec` command.\n\n### Modifying and Testing CI\n\nIf making changes to CI that cannot be done on a branch (eg: changes to issue_comment triggers), you can:\n\n1. fork the 'kubeflow-containers' repo\n2. Modify the CI with\n\n- REGISTRY: (your own dockerhub repo, eg: \"j-smith\" (no need for the full url))\n- Change\n  ```\n  - uses: azure/docker-login@v1\n    with:\n      login-server: ${{ env.REGISTRY_NAME }}.azurecr.io\n      username: ${{ secrets.REGISTRY_USERNAME }}\n      password: ${{ secrets.REGISTRY_PASSWORD }}\n  ```\n  to\n  ```\n  - uses: docker/login-action@v1\n    with:\n      username: ${{ secrets.REGISTRY_USERNAME }}\n      password: ${{ secrets.REGISTRY_PASSWORD }}\n  ```\n\n3. In your forked repo, define secrets for REGISTRY_USERNAME and REGISTRY_PASSWORD with your dockerhub credentials (you should use an API token, not your actual dockerhub password)\n\n---\n\n## Beta Process\n\n![Flowchart of the beta release process](./docs/images/beta_process_v2.drawio.png)\n\nTo reduce unexpected changes getting added into the images used by our users, \nwe implemented a beta process that should be followed when introducing changes to the codebase.\n\nWhen a change needs to be done, new feature branches should be created from the `beta` branch. \nFollowing this, new pull requests should target the `beta` branch, unless absolutely necessary to target `master` directly.\n\nOnce a pull request has been approved, if the target branch is `beta`, it will automatically be set with the `ready for beta` label.\nThis label will help us track which new additions are heading into beta. \nWith this, the pull request should not be merged manually as an automated process will handle that.\n\nWe have in place a workflow(`beta-auto-merge`) which runs on a schedule and handles merging all the `ready for beta` labelled pull requests into `beta`.\nThis workflow runs every two weeks, and helps us manage the frequency of updates to the `beta` branch.\n\nOnce merged into the beta branch, a workflow will build and tag our container images with the `beta` tag instead of `v2`.\nUsers will then be able to use those `beta` tagged images for their notebook servers if they wish to get early access to new features and fixes.\n\nWe also have a second workflow(`beta-promote`) running on a schedule that handles creating a new pull request to promote the beta branch to master.\nIt also runs every two weeks, but on alternating weeks from the `beta-auto-merge` workflow.\nThis means that new features and fixes should live for about one week on the beta branch before they are made official in master.\n\nOnce we have this new pull request created, someone can manually review it, fix any potential problems, and then finally merge it.\nAfter this pull request is merged, we have a third workflow(`master-release.yaml`) that will handle creating a Github release for `master`.\nThis release can help us communicate what changes have been done to our container images.\n\n## Other Development Notes\n\n### Github CI\n\nThe Github workflow is set up to build the images and their dependant stages.\nSee below for a flowchart of this build.\n\nThe main workflow is `docker.yaml`,\nit controls the stage build order, and what triggers the CI.\n(Pushes to master, pushes to an open pull-request, and nightly builds)\n\nThe building of a stage is controled by `docker-steps.yaml`.\nIt checks if there are changes to the stage or dependant stages.\nBuilds a new image if there are changes, \nor pulls a copy of the existing image if not.\nTesting will be performed if this is the final stage in the build of an image.\n\n![A flowchart of the Github CI workflow](./docs/images/Workflows.png)\n\n### The `v2` and `latest` tags for the master branch\n\n\nThese tags are intended to be `long-lived` in that they will not change.\nSubsequent pushes will clobber the previous `IMAGENAME:v2` image.\nThis means that `IMAGENAME:v2` will be updated automatically as changes are made,\nso updates to the tag are not needed.\n\nA new `v3` tag will be created for adding these breaking changes.\n\n**Note**:\nThe `latest` tag is shared with [aaw-kubeflow-containers](https://github.com/StatCan/aaw-kubeflow-containers),\nSo isn't reliable\n\n---\n### Set User File Permissions\n\nThe Dockerfiles in this repo are intended to construct compute environments for a non-root user **jovyan**\nto ensure the end user has the least privileges required for their task,\nbut installation of some of the software needed by the user must be done as the **root** user.\nThis means that installation of anything that should be user editable\n(eg: `pip` and `conda` installs, additional files in `/home/$NB_USER`, etc.)\nwill by default be owned by **root** and not modifiable by **jovyan**.\n**Therefore we must change the permissions of these files to allow the user specific access for modification.**\n\nFor example, most pip install/conda install commands occur as the root user\nand result in new files in the $CONDA_DIR directory that will be owned by **root**.\nThis will cause issues if user **jovyan** tried to update or uninstall these packages\n(as they by default will not have permission to change/remove these files).\n\nTo fix this issue, end any `RUN` command that edits any user-editable files with:\n\n```\nfix-permissions $CONDA_DIR \u0026\u0026 \\\nfix-permissions /home/$NB_USER\n```\n\nThis fix edits the permissions of files in these locations to allow user access.\nNote that if these are not applied **in the same layer as when the new files were added**\nit will result in a duplication of data in the layer\nbecause the act of changing permissions on a file from a previous layer requires a copy of that file into the current layer.\nSo something like:\n\n```\nRUN add_1GB_file_with_wrong_permissions_to_NB_USER.sh \u0026\u0026 \\\n\tfix-permissions /home/$NB_USER\n```\n\nwould add a single layer of about 1GB, whereas\n\n```\nRUN add_1GB_file_with_wrong_permissions_to_NB_USER.sh\n\nRUN fix-permissions /home/$NB_USER\n```\n\nwould add two layers, each about 1GB (2GB total).\n\n### Troubleshooting\n\nIf running using a VM and RStudio image was built successfully but is not opening correctly on localhost (error 5000 page),\nchange your CPU allocation in your Linux VM settings to \u003e= 3.\nYou can also use your VM's system monitor to examine if all CPUs are 100% being used as your container is running.\nIf so, increase CPU allocation.\nThis was tested on Linux Ubuntu 20.04 virtual machine.\n\n## Structure\n\n```\n.\n├── .github/workflow                        # Github CI. Controls the stage build order\n│\n├── Makefile                                # Controls the interactions with docker commands\n│\n├── make_helpers                            # Scripts used by makefile\n│   ├── get_branch_name.sh\n│   ├── get-nvidia-stuff.sh\n│   └── post-build-hook.sh\n│\n├── images                                  # Dockerfile and required resources for stage builds\n│   ├── base                                # Common base of the images\n│   ├── jupyterlab                          # Jupyterlab specific Dockerfile\n│   ├── mid                                 # Common mid point for all images\n│   ├── sas                                 # SAS specific Dockerfile\n|   └── sas_kernel                          # Dockerfile for installation of sas_kernel\n│\n├── docs                                    # files/images used in documentation (ex. Readme's)\n│\n└── tests\n    ├── general/                            # General tests applied to all images\n    ├── jupyterlab-cpu/                     # Test applied to a specific image\n    └── README.md\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstatcan%2Fzone-kubeflow-containers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstatcan%2Fzone-kubeflow-containers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstatcan%2Fzone-kubeflow-containers/lists"}