Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Helm chart jupyterhub to 4.0.0 #7631

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Nov 7, 2024

This PR contains the following updates:

Package Update Change
jupyterhub (source) major 2.0.0 -> 4.0.0

Warning

Some dependencies could not be looked up. Check the Dependency Dashboard for more information.


Release Notes

jupyterhub/zero-to-jupyterhub-k8s (jupyterhub)

v4.0.0

Compare Source

Please see the changelog for details.

v3.3.8

Compare Source

Please see the changelog for details.

v3.3.7

Compare Source

Please see the changelog for details.

v3.3.6

Compare Source

Please see the changelog for details.

v3.3.5

Compare Source

Please see the changelog for details.

v3.3.4

Compare Source

Please see the changelog for details.

v3.3.3

Compare Source

Please see the changelog for details.

v3.3.2

Compare Source

Please see the changelog for details.

v3.3.1

Compare Source

Please see the changelog for details.

v3.3.0

Compare Source

Please see the changelog for details.

v3.2.1

Compare Source

Please see the changelog for details.

v3.2.0

Compare Source

Please see the changelog for details.

v3.1.0

Compare Source

Please see the changelog for details.

v3.0.3

Compare Source

Please see the changelog for details.

v3.0.2

Compare Source

Please see the changelog for details.

v3.0.1

Compare Source

Please see the changelog for details.

v3.0.0

Compare Source

Please see the changelog for details.


Configuration

📅 Schedule: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

Signed-off-by: Zee Aslam <h3mmy@users.noreply.github.com>
@renovate renovate bot requested a review from h3mmy as a code owner November 7, 2024 11:52
@bloopy-boi bloopy-boi bot added size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. area/cluster Changes made in the cluster directory labels Nov 7, 2024
@bloopy-boi
Copy link
Contributor

bloopy-boi bot commented Nov 7, 2024

Path: cluster/apps/default/jupyterhub/helm-release.yaml
Version: 2.0.0 -> 4.0.0

@@ -1,2215 +1 @@
----
-# Source: jupyterhub/templates/hub/netpol.yaml
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- podSelector:
- matchLabels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- policyTypes:
- - Ingress
- - Egress
- # IMPORTANT:
- # NetworkPolicy's ingress "from" and egress "to" rule specifications require
- # great attention to detail. A quick summary is:
- #
- # 1. You can provide "from"/"to" rules that provide access either ports or a
- # subset of ports.
- # 2. You can for each "from"/"to" rule provide any number of
- # "sources"/"destinations" of four different kinds.
- # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy
- # - namespaceSelector - targets all pods running in namespaces with a certain label
- # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label
- # - ipBlock - targets network traffic from/to a set of IP address ranges
- #
- # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors
- #
- ingress:
- # allowed pods (hub.jupyter.org/network-access-hub) --> hub
- - ports:
- - port: http
- from:
- # source 1 - labeled pods
- - podSelector:
- matchLabels:
- hub.jupyter.org/network-access-hub: "true"
- egress:
- # hub --> proxy
- - to:
- - podSelector:
- matchLabels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8001
- # hub --> singleuser-server
- - to:
- - podSelector:
- matchLabels:
- component: singleuser-server
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8888
- # Allow outbound connections to the DNS port in the private IP ranges
- - ports:
- - protocol: UDP
- port: 53
- - protocol: TCP
- port: 53
- to:
- - ipBlock:
- cidr: 10.0.0.0/8
- - ipBlock:
- cidr: 172.16.0.0/12
- - ipBlock:
- cidr: 192.168.0.0/16
- # Allow outbound connections to non-private IP ranges
- - to:
- - ipBlock:
- cidr: 0.0.0.0/0
- except:
- # As part of this rule, don't:
- # - allow outbound connections to private IP
- - 10.0.0.0/8
- - 172.16.0.0/12
- - 192.168.0.0/16
- # - allow outbound connections to the cloud metadata server
- - 169.254.169.254/32
- # Allow outbound connections to private IP ranges
- - to:
- - ipBlock:
- cidr: 10.0.0.0/8
- - ipBlock:
- cidr: 172.16.0.0/12
- - ipBlock:
- cidr: 192.168.0.0/16
- # Allow outbound connections to the cloud metadata server
- - to:
- - ipBlock:
- cidr: 169.254.169.254/32
----
-# Source: jupyterhub/templates/proxy/netpol.yaml
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: proxy
- labels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- podSelector:
- matchLabels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- policyTypes:
- - Ingress
- - Egress
- # IMPORTANT:
- # NetworkPolicy's ingress "from" and egress "to" rule specifications require
- # great attention to detail. A quick summary is:
- #
- # 1. You can provide "from"/"to" rules that provide access either ports or a
- # subset of ports.
- # 2. You can for each "from"/"to" rule provide any number of
- # "sources"/"destinations" of four different kinds.
- # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy
- # - namespaceSelector - targets all pods running in namespaces with a certain label
- # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label
- # - ipBlock - targets network traffic from/to a set of IP address ranges
- #
- # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors
- #
- ingress:
- # allow incoming traffic to these ports independent of source
- - ports:
- - port: http
- - port: https
- # allowed pods (hub.jupyter.org/network-access-proxy-http) --> proxy (http/https port)
- - ports:
- - port: http
- from:
- # source 1 - labeled pods
- - podSelector:
- matchLabels:
- hub.jupyter.org/network-access-proxy-http: "true"
- # allowed pods (hub.jupyter.org/network-access-proxy-api) --> proxy (api port)
- - ports:
- - port: api
- from:
- # source 1 - labeled pods
- - podSelector:
- matchLabels:
- hub.jupyter.org/network-access-proxy-api: "true"
- egress:
- # proxy --> hub
- - to:
- - podSelector:
- matchLabels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8081
- # proxy --> singleuser-server
- - to:
- - podSelector:
- matchLabels:
- component: singleuser-server
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8888
- # Allow outbound connections to the DNS port in the private IP ranges
- - ports:
- - protocol: UDP
- port: 53
- - protocol: TCP
- port: 53
- to:
- - ipBlock:
- cidr: 10.0.0.0/8
- - ipBlock:
- cidr: 172.16.0.0/12
- - ipBlock:
- cidr: 192.168.0.0/16
- # Allow outbound connections to non-private IP ranges
- - to:
- - ipBlock:
- cidr: 0.0.0.0/0
- except:
- # As part of this rule, don't:
- # - allow outbound connections to private IP
- - 10.0.0.0/8
- - 172.16.0.0/12
- - 192.168.0.0/16
- # - allow outbound connections to the cloud metadata server
- - 169.254.169.254/32
- # Allow outbound connections to private IP ranges
- - to:
- - ipBlock:
- cidr: 10.0.0.0/8
- - ipBlock:
- cidr: 172.16.0.0/12
- - ipBlock:
- cidr: 192.168.0.0/16
- # Allow outbound connections to the cloud metadata server
- - to:
- - ipBlock:
- cidr: 169.254.169.254/32
----
-# Source: jupyterhub/templates/singleuser/netpol.yaml
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: singleuser
- labels:
- component: singleuser
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- podSelector:
- matchLabels:
- component: singleuser-server
- app: jupyterhub
- release: jupyterhub
- policyTypes:
- - Ingress
- - Egress
- # IMPORTANT:
- # NetworkPolicy's ingress "from" and egress "to" rule specifications require
- # great attention to detail. A quick summary is:
- #
- # 1. You can provide "from"/"to" rules that provide access either ports or a
- # subset of ports.
- # 2. You can for each "from"/"to" rule provide any number of
- # "sources"/"destinations" of four different kinds.
- # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy
- # - namespaceSelector - targets all pods running in namespaces with a certain label
- # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label
- # - ipBlock - targets network traffic from/to a set of IP address ranges
- #
- # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors
- #
- ingress:
- # allowed pods (hub.jupyter.org/network-access-singleuser) --> singleuser-server
- - ports:
- - port: notebook-port
- from:
- # source 1 - labeled pods
- - podSelector:
- matchLabels:
- hub.jupyter.org/network-access-singleuser: "true"
- egress:
- # singleuser-server --> hub
- - to:
- - podSelector:
- matchLabels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8081
- # singleuser-server --> proxy
- # singleuser-server --> autohttps
- #
- # While not critical for core functionality, a user or library code may rely
- # on communicating with the proxy or autohttps pods via a k8s Service it can
- # detected from well known environment variables.
- #
- - to:
- - podSelector:
- matchLabels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8000
- - to:
- - podSelector:
- matchLabels:
- component: autohttps
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8080
- - port: 8443
- # Allow outbound connections to the DNS port in the private IP ranges
- - ports:
- - protocol: UDP
- port: 53
- - protocol: TCP
- port: 53
- to:
- - ipBlock:
- cidr: 10.0.0.0/8
- - ipBlock:
- cidr: 172.16.0.0/12
- - ipBlock:
- cidr: 192.168.0.0/16
- # Allow outbound connections to non-private IP ranges
- - to:
- - ipBlock:
- cidr: 0.0.0.0/0
- except:
- # As part of this rule, don't:
- # - allow outbound connections to private IP
- - 10.0.0.0/8
- - 172.16.0.0/12
- - 192.168.0.0/16
- # - allow outbound connections to the cloud metadata server
- - 169.254.169.254/32
----
-# Source: jupyterhub/templates/scheduling/user-placeholder/pdb.yaml
-apiVersion: policy/v1beta1
-kind: PodDisruptionBudget
-metadata:
- name: user-placeholder
- labels:
- component: user-placeholder
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- minAvailable: 0
- selector:
- matchLabels:
- component: user-placeholder
- app: jupyterhub
- release: jupyterhub
----
-# Source: jupyterhub/templates/scheduling/user-scheduler/pdb.yaml
-apiVersion: policy/v1beta1
-kind: PodDisruptionBudget
-metadata:
- name: user-scheduler
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- maxUnavailable: 1
- selector:
- matchLabels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
----
-# Source: jupyterhub/templates/hub/serviceaccount.yaml
-apiVersion: v1
-kind: ServiceAccount
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
----
-# Source: jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml
-apiVersion: v1
-kind: ServiceAccount
-metadata:
- name: user-scheduler
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
----
-# Source: jupyterhub/templates/hub/secret.yaml
-kind: Secret
-apiVersion: v1
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-type: Opaque
-data:
- values.yaml: "Q2hhcnQ6CiAgTmFtZToganVweXRlcmh1YgogIFZlcnNpb246IDIuMC4wClJlbGVhc2U6CiAgTmFtZToganVweXRlcmh1YgogIE5hbWVzcGFjZTogZGVmYXVsdAogIFNlcnZpY2U6IEhlbG0KY3VsbDoKICBhZG1pblVzZXJzOiB0cnVlCiAgY29uY3VycmVuY3k6IDEwCiAgZW5hYmxlZDogdHJ1ZQogIGV2ZXJ5OiA2MDAKICBtYXhBZ2U6IDAKICByZW1vdmVOYW1lZFNlcnZlcnM6IGZhbHNlCiAgdGltZW91dDogMzYwMAogIHVzZXJzOiBmYWxzZQpjdXN0b206IHt9CmRlYnVnOgogIGVuYWJsZWQ6IGZhbHNlCmZ1bGxuYW1lT3ZlcnJpZGU6ICIiCmdsb2JhbDoKICBzYWZlVG9TaG93VmFsdWVzOiBmYWxzZQpodWI6CiAgYWN0aXZlU2VydmVyTGltaXQ6IG51bGwKICBhbGxvd05hbWVkU2VydmVyczogZmFsc2UKICBhbm5vdGF0aW9uczoge30KICBhcmdzOiBbXQogIGF1dGhlbnRpY2F0ZVByb21ldGhldXM6IGZhbHNlCiAgYmFzZVVybDogLwogIGNvbW1hbmQ6IFtdCiAgY29uY3VycmVudFNwYXduTGltaXQ6IDY0CiAgY29uZmlnOgogICAgQXV0aGVudGljYXRvcjoKICAgICAgYWRtaW5fdXNlcnM6CiAgICAgIC0gJHtKSFVCX0FETUlOfQogICAgICAtICR7SkhVQl9BRE1JTjJ9CiAgICAgIC0gJHtKSFVCX0FETUlOM30KICAgIEdlbmVyaWNPQXV0aGVudGljYXRvcjoKICAgICAgYXV0aG9yaXplX3VybDogaHR0cHM6Ly9hdXRoLiR7WFlaX0RPTUFJTn0vYXBwbGljYXRpb24vby9hdXRob3JpemUvCiAgICAgIGNsaWVudF9pZDogJHtKSFVCX09BVVRIX0NMSUVOVF9JRH0KICAgICAgY2xpZW50X3NlY3JldDogJHtKSFVCX09BVVRIX0NMSUVOVF9TRUNSRVR9CiAgICAgIG9hdXRoX2NhbGxiYWNrX3VybDogaHR0cHM6Ly9qaHViLiR7WFlaX0RPTUFJTn0vaHViL29hdXRoX2NhbGxiYWNrCiAgICAgIHNjb3BlOgogICAgICAtIG9wZW5pZAogICAgICAtIHByb2ZpbGUKICAgICAgLSBlbWFpbAogICAgICB0b2tlbl91cmw6IGh0dHBzOi8vYXV0aC4ke1hZWl9ET01BSU59L2FwcGxpY2F0aW9uL28vdG9rZW4vCiAgICAgIHVzZXJkYXRhX3VybDogaHR0cHM6Ly9hdXRoLiR7WFlaX0RPTUFJTn0vYXBwbGljYXRpb24vby91c2VyaW5mby8KICAgICAgdXNlcm5hbWVfa2V5OiBwcmVmZXJyZWRfdXNlcm5hbWUKICAgIEp1cHl0ZXJIdWI6CiAgICAgIGFkbWluX2FjY2VzczogdHJ1ZQogICAgICBhdXRoZW50aWNhdG9yX2NsYXNzOiBnZW5lcmljLW9hdXRoCiAgY29uc2VjdXRpdmVGYWlsdXJlTGltaXQ6IDUKICBjb250YWluZXJTZWN1cml0eUNvbnRleHQ6CiAgICBhbGxvd1ByaXZpbGVnZUVzY2FsYXRpb246IGZhbHNlCiAgICBydW5Bc0dyb3VwOiAxMDAwCiAgICBydW5Bc1VzZXI6IDEwMDAKICBjb29raWVTZWNyZXQ6IG51bGwKICBkYjoKICAgIHBhc3N3b3JkOiBudWxsCiAgICBwdmM6CiAgICAgIGFjY2Vzc01vZGVzOgogICAgICAtIFJlYWRXcml0ZU9uY2UKICAgICAgYW5ub3RhdGlvbnM6IHt9CiAgICAgIHNlbGVjdG9yOiB7fQogICAgICBzdG9yYWdlOiAxR2kKICAgICAgc3RvcmFnZUNsYXNzTmFtZTogbnVsbAogICAgICBzdWJQYXRoOiBudWxsCiAgICB0eXBlOiBteXNxbAogICAgdXBncmFkZTogbnVsbAogICAgdXJsOiAke0pVUFlURVJfREJfVVJMfQogIGRlcGxveW1lbnRTdHJhdGVneToKICAgIHR5cGU6IFJlY3JlYXRlCiAgZXhpc3RpbmdTZWNyZXQ6IG51bGwKICBleHRyYUNvbmZpZzoge30KICBleHRyYUNvbnRhaW5lcnM6IFtdCiAgZXh0cmFFbnY6IHt9CiAgZXh0cmFGaWxlczoge30KICBleHRyYVBvZFNwZWM6IHt9CiAgZXh0cmFWb2x1bWVNb3VudHM6IFtdCiAgZXh0cmFWb2x1bWVzOiBbXQogIGltYWdlOgogICAgbmFtZToganVweXRlcmh1Yi9rOHMtaHViCiAgICBwdWxsUG9saWN5OiBudWxsCiAgICBwdWxsU2VjcmV0czogW10KICAgIHRhZzogMi4wLjAKICBpbml0Q29udGFpbmVyczogW10KICBsYWJlbHM6IHt9CiAgbGlmZWN5Y2xlOiB7fQogIGxpdmVuZXNzUHJvYmU6CiAgICBlbmFibGVkOiB0cnVlCiAgICBmYWlsdXJlVGhyZXNob2xkOiAzMAogICAgaW5pdGlhbERlbGF5U2Vjb25kczogMzAwCiAgICBwZXJpb2RTZWNvbmRzOiAxMAogICAgdGltZW91dFNlY29uZHM6IDMKICBsb2FkUm9sZXM6IHt9CiAgbmFtZWRTZXJ2ZXJMaW1pdFBlclVzZXI6IG51bGwKICBuZXR3b3JrUG9saWN5OgogICAgYWxsb3dlZEluZ3Jlc3NQb3J0czogW10KICAgIGVncmVzczogW10KICAgIGVncmVzc0FsbG93UnVsZXM6CiAgICAgIGNsb3VkTWV0YWRhdGFTZXJ2ZXI6IHRydWUKICAgICAgZG5zUG9ydHNQcml2YXRlSVBzOiB0cnVlCiAgICAgIG5vblByaXZhdGVJUHM6IHRydWUKICAgICAgcHJpdmF0ZUlQczogdHJ1ZQogICAgZW5hYmxlZDogdHJ1ZQogICAgaW5ncmVzczogW10KICAgIGludGVyTmFtZXNwYWNlQWNjZXNzTGFiZWxzOiBpZ25vcmUKICBub2RlU2VsZWN0b3I6CiAgICBrdWJlcm5ldGVzLmlvL2FyY2g6IGFtZDY0CiAgcGRiOgogICAgZW5hYmxlZDogZmFsc2UKICAgIG1heFVuYXZhaWxhYmxlOiBudWxsCiAgICBtaW5BdmFpbGFibGU6IDEKICBwb2RTZWN1cml0eUNvbnRleHQ6CiAgICBmc0dyb3VwOiAxMDAwCiAgcmVhZGluZXNzUHJvYmU6CiAgICBlbmFibGVkOiB0cnVlCiAgICBmYWlsdXJlVGhyZXNob2xkOiAxMDAwCiAgICBpbml0aWFsRGVsYXlTZWNvbmRzOiAwCiAgICBwZXJpb2RTZWNvbmRzOiAyCiAgICB0aW1lb3V0U2Vjb25kczogMQogIHJlZGlyZWN0VG9TZXJ2ZXI6IG51bGwKICByZXNvdXJjZXM6IHt9CiAgcmV2aXNpb25IaXN0b3J5TGltaXQ6IG51bGwKICBzZXJ2aWNlOgogICAgYW5ub3RhdGlvbnM6IHt9CiAgICBleHRyYVBvcnRzOiBbXQogICAgbG9hZEJhbGFuY2VySVA6IG51bGwKICAgIHBvcnRzOgogICAgICBub2RlUG9ydDogbnVsbAogICAgdHlwZTogQ2x1c3RlcklQCiAgc2VydmljZUFjY291bnQ6CiAgICBhbm5vdGF0aW9uczoge30KICAgIGNyZWF0ZTogdHJ1ZQogICAgbmFtZTogbnVsbAogIHNlcnZpY2VzOiB7fQogIHNodXRkb3duT25Mb2dvdXQ6IG51bGwKICB0ZW1wbGF0ZVBhdGhzOiBbXQogIHRlbXBsYXRlVmFyczoge30KICB0b2xlcmF0aW9uczogW10KaW1hZ2VQdWxsU2VjcmV0OgogIGF1dG9tYXRpY1JlZmVyZW5jZUluamVjdGlvbjogdHJ1ZQogIGNyZWF0ZTogZmFsc2UKICBlbWFpbDogbnVsbAogIHBhc3N3b3JkOiBudWxsCiAgcmVnaXN0cnk6IG51bGwKICB1c2VybmFtZTogbnVsbAppbWFnZVB1bGxTZWNyZXRzOiBbXQppbmdyZXNzOgogIGFubm90YXRpb25zOgogICAgY2VydC1tYW5hZ2VyLmlvL2NsdXN0ZXItaXNzdWVyOiBsZXRzZW5jcnlwdC1wcm9kdWN0aW9uCiAgICBoYWppbWFyaS5pby9hcHBOYW1lOiBqdXB5dGVyaHViCiAgICBoYWppbWFyaS5pby9lbmFibGU6ICJ0cnVlIgogICAgaGFqaW1hcmkuaW8vaWNvbjogY2hhcnQtc2NhdHRlci1wbG90CiAgICBoYWppbWFyaS5pby9pbnN0YW5jZTogYmxvb3AteHl6CiAgICBrdWJlcm5ldGVzLmlvL2luZ3Jlc3MuY2xhc3M6IHRyYWVmaWsKICAgIHRyYWVmaWsuaW5ncmVzcy5rdWJlcm5ldGVzLmlvL3JvdXRlci5lbnRyeXBvaW50czogd2Vic2VjdXJlCiAgICB0cmFlZmlrLmluZ3Jlc3Mua3ViZXJuZXRlcy5pby9yb3V0ZXIubWlkZGxld2FyZXM6IG5ldHdvcmtpbmctY2hhaW4tbm8tYXV0aEBrdWJlcm5ldGVzY3JkCiAgZW5hYmxlZDogdHJ1ZQogIGhvc3RzOgogIC0gamh1Yi4ke1hZWl9ET01BSU59CiAgaW5ncmVzc0NsYXNzTmFtZTogbnVsbAogIHBhdGhTdWZmaXg6IG51bGwKICBwYXRoVHlwZTogUHJlZml4CiAgdGxzOgogIC0gaG9zdHM6CiAgICAtIGpodWIuJHtYWVpfRE9NQUlOfQogICAgc2VjcmV0TmFtZTogdGxzLmpodWIKcHJlUHVsbGVyOgogIGFubm90YXRpb25zOiB7fQogIGNvbnRhaW5lclNlY3VyaXR5Q29udGV4dDoKICAgIGFsbG93UHJpdmlsZWdlRXNjYWxhdGlvbjogZmFsc2UKICAgIHJ1bkFzR3JvdXA6IDY1NTM0CiAgICBydW5Bc1VzZXI6IDY1NTM0CiAgY29udGludW91czoKICAgIGVuYWJsZWQ6IHRydWUKICBleHRyYUltYWdlczoge30KICBleHRyYVRvbGVyYXRpb25zOiBbXQogIGhvb2s6CiAgICBjb250YWluZXJTZWN1cml0eUNvbnRleHQ6CiAgICAgIGFsbG93UHJpdmlsZWdlRXNjYWxhdGlvbjogZmFsc2UKICAgICAgcnVuQXNHcm91cDogNjU1MzQKICAgICAgcnVuQXNVc2VyOiA2NTUzNAogICAgZW5hYmxlZDogdHJ1ZQogICAgaW1hZ2U6CiAgICAgIG5hbWU6IGp1cHl0ZXJodWIvazhzLWltYWdlLWF3YWl0ZXIKICAgICAgcHVsbFBvbGljeTogbnVsbAogICAgICBwdWxsU2VjcmV0czogW10KICAgICAgdGFnOiAyLjAuMAogICAgbm9kZVNlbGVjdG9yOgogICAgICBrdWJlcm5ldGVzLmlvL2FyY2g6IGFtZDY0CiAgICBwb2RTY2hlZHVsaW5nV2FpdER1cmF0aW9uOiAxMAogICAgcHVsbE9ubHlPbkNoYW5nZXM6IHRydWUKICAgIHJlc291cmNlczoge30KICAgIHNlcnZpY2VBY2NvdW50OgogICAgICBhbm5vdGF0aW9uczoge30KICAgICAgY3JlYXRlOiB0cnVlCiAgICAgIG5hbWU6IG51bGwKICAgIHRvbGVyYXRpb25zOiBbXQogIGxhYmVsczoge30KICBwYXVzZToKICAgIGNvbnRhaW5lclNlY3VyaXR5Q29udGV4dDoKICAgICAgYWxsb3dQcml2aWxlZ2VFc2NhbGF0aW9uOiBmYWxzZQogICAgICBydW5Bc0dyb3VwOiA2NTUzNAogICAgICBydW5Bc1VzZXI6IDY1NTM0CiAgICBpbWFnZToKICAgICAgbmFtZTogazhzLmdjci5pby9wYXVzZQogICAgICBwdWxsUG9saWN5OiBudWxsCiAgICAgIHB1bGxTZWNyZXRzOiBbXQogICAgICB0YWc6ICIzLjgiCiAgcHVsbFByb2ZpbGVMaXN0SW1hZ2VzOiB0cnVlCiAgcmVzb3VyY2VzOiB7fQogIHJldmlzaW9uSGlzdG9yeUxpbWl0OiBudWxsCnByb3h5OgogIGFubm90YXRpb25zOiB7fQogIGNocDoKICAgIGNvbnRhaW5lclNlY3VyaXR5Q29udGV4dDoKICAgICAgYWxsb3dQcml2aWxlZ2VFc2NhbGF0aW9uOiBmYWxzZQogICAgICBydW5Bc0dyb3VwOiA2NTUzNAogICAgICBydW5Bc1VzZXI6IDY1NTM0CiAgICBkZWZhdWx0VGFyZ2V0OiBudWxsCiAgICBlcnJvclRhcmdldDogbnVsbAogICAgZXh0cmFDb21tYW5kTGluZUZsYWdzOiBbXQogICAgZXh0cmFFbnY6IHt9CiAgICBleHRyYVBvZFNwZWM6IHt9CiAgICBpbWFnZToKICAgICAgbmFtZToganVweXRlcmh1Yi9jb25maWd1cmFibGUtaHR0cC1wcm94eQogICAgICBwdWxsUG9saWN5OiBudWxsCiAgICAgIHB1bGxTZWNyZXRzOiBbXQogICAgICB0YWc6IDQuNS4zCiAgICBsaXZlbmVzc1Byb2JlOgogICAgICBlbmFibGVkOiB0cnVlCiAgICAgIGZhaWx1cmVUaHJlc2hvbGQ6IDMwCiAgICAgIGluaXRpYWxEZWxheVNlY29uZHM6IDYwCiAgICAgIHBlcmlvZFNlY29uZHM6IDEwCiAgICAgIHRpbWVvdXRTZWNvbmRzOiAzCiAgICBuZXR3b3JrUG9saWN5OgogICAgICBhbGxvd2VkSW5ncmVzc1BvcnRzOgogICAgICAtIGh0dHAKICAgICAgLSBodHRwcwogICAgICBlZ3Jlc3M6IFtdCiAgICAgIGVncmVzc0FsbG93UnVsZXM6CiAgICAgICAgY2xvdWRNZXRhZGF0YVNlcnZlcjogdHJ1ZQogICAgICAgIGRuc1BvcnRzUHJpdmF0ZUlQczogdHJ1ZQogICAgICAgIG5vblByaXZhdGVJUHM6IHRydWUKICAgICAgICBwcml2YXRlSVBzOiB0cnVlCiAgICAgIGVuYWJsZWQ6IHRydWUKICAgICAgaW5ncmVzczogW10KICAgICAgaW50ZXJOYW1lc3BhY2VBY2Nlc3NMYWJlbHM6IGlnbm9yZQogICAgbm9kZVNlbGVjdG9yOiB7fQogICAgcGRiOgogICAgICBlbmFibGVkOiBmYWxzZQogICAgICBtYXhVbmF2YWlsYWJsZTogbnVsbAogICAgICBtaW5BdmFpbGFibGU6IDEKICAgIHJlYWRpbmVzc1Byb2JlOgogICAgICBlbmFibGVkOiB0cnVlCiAgICAgIGZhaWx1cmVUaHJlc2hvbGQ6IDEwMDAKICAgICAgaW5pdGlhbERlbGF5U2Vjb25kczogMAogICAgICBwZXJpb2RTZWNvbmRzOiAyCiAgICAgIHRpbWVvdXRTZWNvbmRzOiAxCiAgICByZXNvdXJjZXM6IHt9CiAgICByZXZpc2lvbkhpc3RvcnlMaW1pdDogbnVsbAogICAgdG9sZXJhdGlvbnM6IFtdCiAgZGVwbG95bWVudFN0cmF0ZWd5OgogICAgcm9sbGluZ1VwZGF0ZTogbnVsbAogICAgdHlwZTogUmVjcmVhdGUKICBodHRwczoKICAgIGVuYWJsZWQ6IGZhbHNlCiAgICBob3N0czogW10KICAgIGxldHNlbmNyeXB0OgogICAgICBhY21lU2VydmVyOiBodHRwczovL2FjbWUtdjAyLmFwaS5sZXRzZW5jcnlwdC5vcmcvZGlyZWN0b3J5CiAgICAgIGNvbnRhY3RFbWFpbDogbnVsbAogICAgbWFudWFsOgogICAgICBjZXJ0OiBudWxsCiAgICAgIGtleTogbnVsbAogICAgc2VjcmV0OgogICAgICBjcnQ6IHRscy5jcnQKICAgICAga2V5OiB0bHMua2V5CiAgICAgIG5hbWU6IG51bGwKICAgIHR5cGU6IGxldHNlbmNyeXB0CiAgbGFiZWxzOiB7fQogIHNlY3JldFN5bmM6CiAgICBjb250YWluZXJTZWN1cml0eUNvbnRleHQ6CiAgICAgIGFsbG93UHJpdmlsZWdlRXNjYWxhdGlvbjogZmFsc2UKICAgICAgcnVuQXNHcm91cDogNjU1MzQKICAgICAgcnVuQXNVc2VyOiA2NTUzNAogICAgaW1hZ2U6CiAgICAgIG5hbWU6IGp1cHl0ZXJodWIvazhzLXNlY3JldC1zeW5jCiAgICAgIHB1bGxQb2xpY3k6IG51bGwKICAgICAgcHVsbFNlY3JldHM6IFtdCiAgICAgIHRhZzogMi4wLjAKICAgIHJlc291cmNlczoge30KICBzZWNyZXRUb2tlbjogbnVsbAogIHNlcnZpY2U6CiAgICBhbm5vdGF0aW9uczoge30KICAgIGRpc2FibGVIdHRwUG9ydDogZmFsc2UKICAgIGV4dHJhUG9ydHM6IFtdCiAgICBsYWJlbHM6IHt9CiAgICBsb2FkQmFsYW5jZXJJUDogbnVsbAogICAgbG9hZEJhbGFuY2VyU291cmNlUmFuZ2VzOiBbXQogICAgbm9kZVBvcnRzOgogICAgICBodHRwOiBudWxsCiAgICAgIGh0dHBzOiBudWxsCiAgICB0eXBlOiBMb2FkQmFsYW5jZXIKICB0cmFlZmlrOgogICAgY29udGFpbmVyU2VjdXJpdHlDb250ZXh0OgogICAgICBhbGxvd1ByaXZpbGVnZUVzY2FsYXRpb246IGZhbHNlCiAgICAgIHJ1bkFzR3JvdXA6IDY1NTM0CiAgICAgIHJ1bkFzVXNlcjogNjU1MzQKICAgIGV4dHJhRHluYW1pY0NvbmZpZzoge30KICAgIGV4dHJhRW52OiB7fQogICAgZXh0cmFJbml0Q29udGFpbmVyczogW10KICAgIGV4dHJhUG9kU3BlYzoge30KICAgIGV4dHJhUG9ydHM6IFtdCiAgICBleHRyYVN0YXRpY0NvbmZpZzoge30KICAgIGV4dHJhVm9sdW1lTW91bnRzOiBbXQogICAgZXh0cmFWb2x1bWVzOiBbXQogICAgaHN0czoKICAgICAgaW5jbHVkZVN1YmRvbWFpbnM6IGZhbHNlCiAgICAgIG1heEFnZTogMTU3MjQ4MDAKICAgICAgcHJlbG9hZDogZmFsc2UKICAgIGltYWdlOgogICAgICBuYW1lOiB0cmFlZmlrCiAgICAgIHB1bGxQb2xpY3k6IG51bGwKICAgICAgcHVsbFNlY3JldHM6IFtdCiAgICAgIHRhZzogdjIuOC40CiAgICBsYWJlbHM6IHt9CiAgICBuZXR3b3JrUG9saWN5OgogICAgICBhbGxvd2VkSW5ncmVzc1BvcnRzOgogICAgICAtIGh0dHAKICAgICAgLSBodHRwcwogICAgICBlZ3Jlc3M6IFtdCiAgICAgIGVncmVzc0FsbG93UnVsZXM6CiAgICAgICAgY2xvdWRNZXRhZGF0YVNlcnZlcjogdHJ1ZQogICAgICAgIGRuc1BvcnRzUHJpdmF0ZUlQczogdHJ1ZQogICAgICAgIG5vblByaXZhdGVJUHM6IHRydWUKICAgICAgICBwcml2YXRlSVBzOiB0cnVlCiAgICAgIGVuYWJsZWQ6IHRydWUKICAgICAgaW5ncmVzczogW10KICAgICAgaW50ZXJOYW1lc3BhY2VBY2Nlc3NMYWJlbHM6IGlnbm9yZQogICAgbm9kZVNlbGVjdG9yOiB7fQogICAgcGRiOgogICAgICBlbmFibGVkOiBmYWxzZQogICAgICBtYXhVbmF2YWlsYWJsZTogbnVsbAogICAgICBtaW5BdmFpbGFibGU6IDEKICAgIHJlc291cmNlczoge30KICAgIHJldmlzaW9uSGlzdG9yeUxpbWl0OiBudWxsCiAgICBzZXJ2aWNlQWNjb3VudDoKICAgICAgYW5ub3RhdGlvbnM6IHt9CiAgICAgIGNyZWF0ZTogdHJ1ZQogICAgICBuYW1lOiBudWxsCiAgICB0b2xlcmF0aW9uczogW10KcmJhYzoKICBjcmVhdGU6IHRydWUKc2NoZWR1bGluZzoKICBjb3JlUG9kczoKICAgIG5vZGVBZmZpbml0eToKICAgICAgbWF0Y2hOb2RlUHVycG9zZTogcHJlZmVyCiAgICB0b2xlcmF0aW9uczoKICAgIC0gZWZmZWN0OiBOb1NjaGVkdWxlCiAgICAgIGtleTogaHViLmp1cHl0ZXIub3JnL2RlZGljYXRlZAogICAgICBvcGVyYXRvcjogRXF1YWwKICAgICAgdmFsdWU6IGNvcmUKICAgIC0gZWZmZWN0OiBOb1NjaGVkdWxlCiAgICAgIGtleTogaHViLmp1cHl0ZXIub3JnX2RlZGljYXRlZAogICAgICBvcGVyYXRvcjogRXF1YWwKICAgICAgdmFsdWU6IGNvcmUKICBwb2RQcmlvcml0eToKICAgIGRlZmF1bHRQcmlvcml0eTogMAogICAgZW5hYmxlZDogZmFsc2UKICAgIGdsb2JhbERlZmF1bHQ6IGZhbHNlCiAgICBpbWFnZVB1bGxlclByaW9yaXR5OiAtNQogICAgdXNlclBsYWNlaG9sZGVyUHJpb3JpdHk6IC0xMAogIHVzZXJQbGFjZWhvbGRlcjoKICAgIGFubm90YXRpb25zOiB7fQogICAgY29udGFpbmVyU2VjdXJpdHlDb250ZXh0OgogICAgICBhbGxvd1ByaXZpbGVnZUVzY2FsYXRpb246IGZhbHNlCiAgICAgIHJ1bkFzR3JvdXA6IDY1NTM0CiAgICAgIHJ1bkFzVXNlcjogNjU1MzQKICAgIGVuYWJsZWQ6IHRydWUKICAgIGltYWdlOgogICAgICBuYW1lOiBrOHMuZ2NyLmlvL3BhdXNlCiAgICAgIHB1bGxQb2xpY3k6IG51bGwKICAgICAgcHVsbFNlY3JldHM6IFtdCiAgICAgIHRhZzogIjMuOCIKICAgIGxhYmVsczoge30KICAgIHJlcGxpY2FzOiAwCiAgICByZXNvdXJjZXM6IHt9CiAgICByZXZpc2lvbkhpc3RvcnlMaW1pdDogbnVsbAogIHVzZXJQb2RzOgogICAgbm9kZUFmZmluaXR5OgogICAgICBtYXRjaE5vZGVQdXJwb3NlOiBwcmVmZXIKICAgIHRvbGVyYXRpb25zOgogICAgLSBlZmZlY3Q6IE5vU2NoZWR1bGUKICAgICAga2V5OiBodWIuanVweXRlci5vcmcvZGVkaWNhdGVkCiAgICAgIG9wZXJhdG9yOiBFcXVhbAogICAgICB2YWx1ZTogdXNlcgogICAgLSBlZmZlY3Q6IE5vU2NoZWR1bGUKICAgICAga2V5OiBodWIuanVweXRlci5vcmdfZGVkaWNhdGVkCiAgICAgIG9wZXJhdG9yOiBFcXVhbAogICAgICB2YWx1ZTogdXNlcgogIHVzZXJTY2hlZHVsZXI6CiAgICBhbm5vdGF0aW9uczoge30KICAgIGNvbnRhaW5lclNlY3VyaXR5Q29udGV4dDoKICAgICAgYWxsb3dQcml2aWxlZ2VFc2NhbGF0aW9uOiBmYWxzZQogICAgICBydW5Bc0dyb3VwOiA2NTUzNAogICAgICBydW5Bc1VzZXI6IDY1NTM0CiAgICBlbmFibGVkOiB0cnVlCiAgICBleHRyYVBvZFNwZWM6IHt9CiAgICBpbWFnZToKICAgICAgbmFtZTogazhzLmdjci5pby9rdWJlLXNjaGVkdWxlcgogICAgICBwdWxsUG9saWN5OiBudWxsCiAgICAgIHB1bGxTZWNyZXRzOiBbXQogICAgICB0YWc6IHYxLjIzLjEwCiAgICBsYWJlbHM6IHt9CiAgICBsb2dMZXZlbDogNAogICAgbm9kZVNlbGVjdG9yOiB7fQogICAgcGRiOgogICAgICBlbmFibGVkOiB0cnVlCiAgICAgIG1heFVuYXZhaWxhYmxlOiAxCiAgICAgIG1pbkF2YWlsYWJsZTogbnVsbAogICAgcGx1Z2luQ29uZmlnOgogICAgLSBhcmdzOgogICAgICAgIHNjb3JpbmdTdHJhdGVneToKICAgICAgICAgIHJlc291cmNlczoKICAgICAgICAgIC0gbmFtZTogY3B1CiAgICAgICAgICAgIHdlaWdodDogMQogICAgICAgICAgLSBuYW1lOiBtZW1vcnkKICAgICAgICAgICAgd2VpZ2h0OiAxCiAgICAgICAgICB0eXBlOiBNb3N0QWxsb2NhdGVkCiAgICAgIG5hbWU6IE5vZGVSZXNvdXJjZXNGaXQKICAgIHBsdWdpbnM6CiAgICAgIHNjb3JlOgogICAgICAgIGRpc2FibGVkOgogICAgICAgIC0gbmFtZTogTm9kZVJlc291cmNlc0JhbGFuY2VkQWxsb2NhdGlvbgogICAgICAgIC0gbmFtZTogTm9kZUFmZmluaXR5CiAgICAgICAgLSBuYW1lOiBJbnRlclBvZEFmZmluaXR5CiAgICAgICAgLSBuYW1lOiBOb2RlUmVzb3VyY2VzRml0CiAgICAgICAgLSBuYW1lOiBJbWFnZUxvY2FsaXR5CiAgICAgICAgZW5hYmxlZDoKICAgICAgICAtIG5hbWU6IE5vZGVBZmZpbml0eQogICAgICAgICAgd2VpZ2h0OiAxNDYzMQogICAgICAgIC0gbmFtZTogSW50ZXJQb2RBZmZpbml0eQogICAgICAgICAgd2VpZ2h0OiAxMzMxCiAgICAgICAgLSBuYW1lOiBOb2RlUmVzb3VyY2VzRml0CiAgICAgICAgICB3ZWlnaHQ6IDEyMQogICAgICAgIC0gbmFtZTogSW1hZ2VMb2NhbGl0eQogICAgICAgICAgd2VpZ2h0OiAxMQogICAgcmVwbGljYXM6IDIKICAgIHJlc291cmNlczoge30KICAgIHJldmlzaW9uSGlzdG9yeUxpbWl0OiBudWxsCiAgICBzZXJ2aWNlQWNjb3VudDoKICAgICAgYW5ub3RhdGlvbnM6IHt9CiAgICAgIGNyZWF0ZTogdHJ1ZQogICAgICBuYW1lOiBudWxsCiAgICB0b2xlcmF0aW9uczogW10Kc2luZ2xldXNlcjoKICBhbGxvd1ByaXZpbGVnZUVzY2FsYXRpb246IGZhbHNlCiAgY2xvdWRNZXRhZGF0YToKICAgIGJsb2NrV2l0aElwdGFibGVzOiB0cnVlCiAgICBpcDogMTY5LjI1NC4xNjkuMjU0CiAgY21kOiBqdXB5dGVyaHViLXNpbmdsZXVzZXIKICBjcHU6CiAgICBndWFyYW50ZWU6IDAuMDUKICAgIGxpbWl0OiA0CiAgZGVmYXVsdFVybDogL2xhYgogIGV2ZW50czogdHJ1ZQogIGV4dHJhQW5ub3RhdGlvbnM6IHt9CiAgZXh0cmFDb250YWluZXJzOiBbXQogIGV4dHJhRW52OgogICAgSlVQWVRFUkhVQl9TSU5HTEVVU0VSX0FQUDoganVweXRlcl9zZXJ2ZXIuc2VydmVyYXBwLlNlcnZlckFwcAogIGV4dHJhRmlsZXM6IHt9CiAgZXh0cmFMYWJlbHM6CiAgICBodWIuanVweXRlci5vcmcvbmV0d29yay1hY2Nlc3MtaHViOiAidHJ1ZSIKICBleHRyYU5vZGVBZmZpbml0eToKICAgIHByZWZlcnJlZDogW10KICAgIHJlcXVpcmVkOgogICAgLSBtYXRjaEV4cHJlc3Npb25zOgogICAgICAtIGtleToga3ViZXJuZXRlcy5pby9hcmNoCiAgICAgICAgb3BlcmF0b3I6IEluCiAgICAgICAgdmFsdWVzOgogICAgICAgIC0gYW1kNjQKICAgICAgICAtIGkzODYKICAgICAgICAtIGk2ODYKICAgICAgICAtIHg4NgogIGV4dHJhUG9kQWZmaW5pdHk6CiAgICBwcmVmZXJyZWQ6IFtdCiAgICByZXF1aXJlZDogW10KICBleHRyYVBvZEFudGlBZmZpbml0eToKICAgIHByZWZlcnJlZDogW10KICAgIHJlcXVpcmVkOiBbXQogIGV4dHJhUG9kQ29uZmlnOiB7fQogIGV4dHJhUmVzb3VyY2U6CiAgICBndWFyYW50ZWVzOiB7fQogICAgbGltaXRzOiB7fQogIGV4dHJhVG9sZXJhdGlvbnM6IFtdCiAgZnNHaWQ6IDEwMAogIGltYWdlOgogICAgbmFtZToganVweXRlci9zY2lweS1ub3RlYm9vawogICAgcHVsbFBvbGljeTogbnVsbAogICAgcHVsbFNlY3JldHM6IFtdCiAgICB0YWc6IDgyY2U3Mzc4OWJhNAogIGluaXRDb250YWluZXJzOiBbXQogIGxpZmVjeWNsZUhvb2tzOiB7fQogIG1lbW9yeToKICAgIGd1YXJhbnRlZTogNTEyTQogICAgbGltaXQ6IDhHCiAgbmV0d29ya1BvbGljeToKICAgIGFsbG93ZWRJbmdyZXNzUG9ydHM6IFtdCiAgICBlZ3Jlc3M6IFtdCiAgICBlZ3Jlc3NBbGxvd1J1bGVzOgogICAgICBjbG91ZE1ldGFkYXRhU2VydmVyOiBmYWxzZQogICAgICBkbnNQb3J0c1ByaXZhdGVJUHM6IHRydWUKICAgICAgbm9uUHJpdmF0ZUlQczogdHJ1ZQogICAgICBwcml2YXRlSVBzOiBmYWxzZQogICAgZW5hYmxlZDogdHJ1ZQogICAgaW5ncmVzczogW10KICAgIGludGVyTmFtZXNwYWNlQWNjZXNzTGFiZWxzOiBpZ25vcmUKICBuZXR3b3JrVG9vbHM6CiAgICBpbWFnZToKICAgICAgbmFtZToganVweXRlcmh1Yi9rOHMtbmV0d29yay10b29scwogICAgICBwdWxsUG9saWN5OiBudWxsCiAgICAgIHB1bGxTZWNyZXRzOiBbXQogICAgICB0YWc6IDIuMC4wCiAgICByZXNvdXJjZXM6IHt9CiAgbm9kZVNlbGVjdG9yOiB7fQogIHBvZE5hbWVUZW1wbGF0ZTogbnVsbAogIHByb2ZpbGVMaXN0OgogIC0gZGVzY3JpcHRpb246ICdUbyBhdm9pZCB0b28gbXVjaCBiZWxscyBhbmQgd2hpc3RsZXM6IFB5dGhvbi4nCiAgICBkaXNwbGF5X25hbWU6IE1pbmltYWwgZW52aXJvbm1lbnQKICAgIGt1YmVzcGF3bmVyX292ZXJyaWRlOgogICAgICBpbWFnZToganVweXRlci9taW5pbWFsLW5vdGVib29rOjgyY2U3Mzc4OWJhNAogIC0gZGVmYXVsdDogdHJ1ZQogICAgZGVzY3JpcHRpb246ICdJZiB5b3Ugd2FudCB0aGUgYWRkaXRpb25hbCBiZWxscyBhbmQgd2hpc3RsZXM6IFB5dGhvbiwgUiwgYW5kIEp1bGlhLicKICAgIGRpc3BsYXlfbmFtZTogRGF0YXNjaWVuY2UgZW52aXJvbm1lbnQKICAtIGRlc2NyaXB0aW9uOiBUaGUgSnVweXRlciBTdGFja3Mgc3BhcmsgaW1hZ2UhCiAgICBkaXNwbGF5X25hbWU6IFNwYXJrIGVudmlyb25tZW50CiAgICBrdWJlc3Bhd25lcl9vdmVycmlkZToKICAgICAgaW1hZ2U6IGp1cHl0ZXIvYWxsLXNwYXJrLW5vdGVib29rOjIzNDNlMzNkZWM0NgogIC0gZGVzY3JpcHRpb246IERhdGFzY2llbmNlIEVudmlyb25tZW50IHdpdGggU2FtcGxlIE5vdGVib29rcwogICAgZGlzcGxheV9uYW1lOiBMZWFybmluZyBEYXRhIFNjaWVuY2UKICAgIGt1YmVzcGF3bmVyX292ZXJyaWRlOgogICAgICBpbWFnZToganVweXRlci9kYXRhc2NpZW5jZS1ub3RlYm9vazoyMzQzZTMzZGVjNDYKICBzZXJ2aWNlQWNjb3VudE5hbWU6IG51bGwKICBzdGFydFRpbWVvdXQ6IDMwMAogIHN0b3JhZ2U6CiAgICBjYXBhY2l0eTogMTVHaQogICAgZHluYW1pYzoKICAgICAgcHZjTmFtZVRlbXBsYXRlOiBjbGFpbS17dXNlcm5hbWV9e3NlcnZlcm5hbWV9CiAgICAgIHN0b3JhZ2VBY2Nlc3NNb2RlczoKICAgICAgLSBSZWFkV3JpdGVPbmNlCiAgICAgIHN0b3JhZ2VDbGFzczogY2VwaC1ibG9jawogICAgICB2b2x1bWVOYW1lVGVtcGxhdGU6IHZvbHVtZS17dXNlcm5hbWV9e3NlcnZlcm5hbWV9CiAgICBleHRyYUxhYmVsczoge30KICAgIGV4dHJhVm9sdW1lTW91bnRzOiBbXQogICAgZXh0cmFWb2x1bWVzOiBbXQogICAgaG9tZU1vdW50UGF0aDogL2hvbWUvam92eWFuCiAgICBzdGF0aWM6CiAgICAgIHB2Y05hbWU6IG51bGwKICAgICAgc3ViUGF0aDogJ3t1c2VybmFtZX0nCiAgICB0eXBlOiBkeW5hbWljCiAgdWlkOiAxMDAw"
- # Any JupyterHub Services api_tokens are exposed in this k8s Secret as a
- # convinience for external services running in the k8s cluster that could
- # mount them directly from this k8s Secret.
 
- # During Helm template rendering, these values that can be autogenerated for
- # users are set using the following logic:
- #
- # 1. Use chart configuration's value
- # 2. Use k8s Secret's value
- # 3. Use a new autogenerated value
- #
- # hub.config.ConfigurableHTTPProxy.auth_token: for hub to proxy-api authorization (JupyterHub.proxy_auth_token is deprecated)
- # hub.config.JupyterHub.cookie_secret: for cookie encryption
- # hub.config.CryptKeeper.keys: for auth state encryption
- #
- hub.config.ConfigurableHTTPProxy.auth_token: "NEpNYThjcVhyZjRiMjVNYzgyNFJReVllRUR1cGI5UUttcEFtdGVJS3cyYllhSlJYTzE5eFNaMzJxTnEyY1FJQQ=="
- hub.config.JupyterHub.cookie_secret: "NDFmN2JhMDNhMTEyN2ViNTE5OTlmMDJlMGRmNDM2MTZmNjI4MTVjNDE2MDQzNDIzZGFlMDZhNTVhYzIwZWNjNA=="
- hub.config.CryptKeeper.keys: "ZWU3NDQ4OTA0ZTExYmZiMDJlNzMwMjdmZmZmZDUzMTliY2Q1ZmExYWU3MTBkNTE1OTZjMTJlNWJjYTdlNTY0Yw=="
----
-# Source: jupyterhub/templates/hub/configmap.yaml
-kind: ConfigMap
-apiVersion: v1
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-data:
- fullname: ""
- fullname-dash: ""
- hub: "hub"
- hub-serviceaccount: "hub"
- hub-existing-secret: ""
- hub-existing-secret-or-default: "hub"
- hub-pvc: "hub-db-dir"
- proxy: "proxy"
- proxy-api: "proxy-api"
- proxy-http: "proxy-http"
- proxy-public: "proxy-public"
- proxy-public-tls: "proxy-public-tls-acme"
- proxy-public-manual-tls: "proxy-public-manual-tls"
- autohttps: "autohttps"
- autohttps-serviceaccount: "autohttps"
- user-scheduler-deploy: "user-scheduler"
- user-scheduler-serviceaccount: "user-scheduler"
- user-scheduler-lock: "user-scheduler-lock"
- user-placeholder: "user-placeholder"
- image-puller-priority: "jupyterhub-image-puller-priority"
- hook-image-awaiter: "hook-image-awaiter"
- hook-image-awaiter-serviceaccount: "hook-image-awaiter"
- hook-image-puller: "hook-image-puller"
- continuous-image-puller: "continuous-image-puller"
- singleuser: "singleuser"
- image-pull-secret: "image-pull-secret"
- ingress: "jupyterhub"
- priority: "jupyterhub-default-priority"
- user-placeholder-priority: "jupyterhub-user-placeholder-priority"
- user-scheduler: "jupyterhub-user-scheduler"
- jupyterhub_config.py: |
- import glob
- import os
- import re
- import sys
- from binascii import a2b_hex
-
- from jupyterhub.utils import url_path_join
- from kubernetes_asyncio import client
- from tornado.httpclient import AsyncHTTPClient
-
- # Make sure that modules placed in the same directory as the jupyterhub config are added to the pythonpath
- configuration_directory = os.path.dirname(os.path.realpath(__file__))
- sys.path.insert(0, configuration_directory)
-
- from z2jh import (
- get_config,
- get_name,
- get_name_env,
- get_secret_value,
- set_config_if_not_none,
- )
-
-
- def camelCaseify(s):
- """convert snake_case to camelCase
-
- For the common case where some_value is set from someValue
- so we don't have to specify the name twice.
- """
- return re.sub(r"_([a-z])", lambda m: m.group(1).upper(), s)
-
-
- # Configure JupyterHub to use the curl backend for making HTTP requests,
- # rather than the pure-python implementations. The default one starts
- # being too slow to make a large number of requests to the proxy API
- # at the rate required.
- AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient")
-
- c.JupyterHub.spawner_class = "kubespawner.KubeSpawner"
-
- # Connect to a proxy running in a different pod. Note that *_SERVICE_*
- # environment variables are set by Kubernetes for Services
- c.ConfigurableHTTPProxy.api_url = (
- f'http://{get_name("proxy-api")}:{get_name_env("proxy-api", "_SERVICE_PORT")}'
- )
- c.ConfigurableHTTPProxy.should_start = False
-
- # Do not shut down user pods when hub is restarted
- c.JupyterHub.cleanup_servers = False
-
- # Check that the proxy has routes appropriately setup
- c.JupyterHub.last_activity_interval = 60
-
- # Don't wait at all before redirecting a spawning user to the progress page
- c.JupyterHub.tornado_settings = {
- "slow_spawn_timeout": 0,
- }
-
-
- # configure the hub db connection
- db_type = get_config("hub.db.type")
- if db_type == "sqlite-pvc":
- c.JupyterHub.db_url = "sqlite:///jupyterhub.sqlite"
- elif db_type == "sqlite-memory":
- c.JupyterHub.db_url = "sqlite://"
- else:
- set_config_if_not_none(c.JupyterHub, "db_url", "hub.db.url")
- db_password = get_secret_value("hub.db.password", None)
- if db_password is not None:
- if db_type == "mysql":
- os.environ["MYSQL_PWD"] = db_password
- elif db_type == "postgres":
- os.environ["PGPASSWORD"] = db_password
- else:
- print(f"Warning: hub.db.password is ignored for hub.db.type={db_type}")
-
-
- # c.JupyterHub configuration from Helm chart's configmap
- for trait, cfg_key in (
- ("concurrent_spawn_limit", None),
- ("active_server_limit", None),
- ("base_url", None),
- ("allow_named_servers", None),
- ("named_server_limit_per_user", None),
- ("authenticate_prometheus", None),
- ("redirect_to_server", None),
- ("shutdown_on_logout", None),
- ("template_paths", None),
- ("template_vars", None),
- ):
- if cfg_key is None:
- cfg_key = camelCaseify(trait)
- set_config_if_not_none(c.JupyterHub, trait, "hub." + cfg_key)
-
- # hub_bind_url configures what the JupyterHub process within the hub pod's
- # container should listen to.
- hub_container_port = 8081
- c.JupyterHub.hub_bind_url = f"http://:{hub_container_port}"
-
- # hub_connect_url is the URL for connecting to the hub for use by external
- # JupyterHub services such as the proxy. Note that *_SERVICE_* environment
- # variables are set by Kubernetes for Services.
- c.JupyterHub.hub_connect_url = (
- f'http://{get_name("hub")}:{get_name_env("hub", "_SERVICE_PORT")}'
- )
-
- # implement common labels
- # this duplicates the jupyterhub.commonLabels helper
- common_labels = c.KubeSpawner.common_labels = {}
- common_labels["app"] = get_config(
- "nameOverride",
- default=get_config("Chart.Name", "jupyterhub"),
- )
- common_labels["heritage"] = "jupyterhub"
- chart_name = get_config("Chart.Name")
- chart_version = get_config("Chart.Version")
- if chart_name and chart_version:
- common_labels["chart"] = "{}-{}".format(
- chart_name,
- chart_version.replace("+", "_"),
- )
- release = get_config("Release.Name")
- if release:
- common_labels["release"] = release
-
- c.KubeSpawner.namespace = os.environ.get("POD_NAMESPACE", "default")
-
- # Max number of consecutive failures before the Hub restarts itself
- # requires jupyterhub 0.9.2
- set_config_if_not_none(
- c.Spawner,
- "consecutive_failure_limit",
- "hub.consecutiveFailureLimit",
- )
-
- for trait, cfg_key in (
- ("pod_name_template", None),
- ("start_timeout", None),
- ("image_pull_policy", "image.pullPolicy"),
- # ('image_pull_secrets', 'image.pullSecrets'), # Managed manually below
- ("events_enabled", "events"),
- ("extra_labels", None),
- ("extra_annotations", None),
- # ("allow_privilege_escalation", None), # Managed manually below
- ("uid", None),
- ("fs_gid", None),
- ("service_account", "serviceAccountName"),
- ("storage_extra_labels", "storage.extraLabels"),
- # ("tolerations", "extraTolerations"), # Managed manually below
- ("node_selector", None),
- ("node_affinity_required", "extraNodeAffinity.required"),
- ("node_affinity_preferred", "extraNodeAffinity.preferred"),
- ("pod_affinity_required", "extraPodAffinity.required"),
- ("pod_affinity_preferred", "extraPodAffinity.preferred"),
- ("pod_anti_affinity_required", "extraPodAntiAffinity.required"),
- ("pod_anti_affinity_preferred", "extraPodAntiAffinity.preferred"),
- ("lifecycle_hooks", None),
- ("init_containers", None),
- ("extra_containers", None),
- ("mem_limit", "memory.limit"),
- ("mem_guarantee", "memory.guarantee"),
- ("cpu_limit", "cpu.limit"),
- ("cpu_guarantee", "cpu.guarantee"),
- ("extra_resource_limits", "extraResource.limits"),
- ("extra_resource_guarantees", "extraResource.guarantees"),
- ("environment", "extraEnv"),
- ("profile_list", None),
- ("extra_pod_config", None),
- ):
- if cfg_key is None:
- cfg_key = camelCaseify(trait)
- set_config_if_not_none(c.KubeSpawner, trait, "singleuser." + cfg_key)
-
- image = get_config("singleuser.image.name")
- if image:
- tag = get_config("singleuser.image.tag")
- if tag:
- image = f"{image}:{tag}"
-
- c.KubeSpawner.image = image
-
- # allow_privilege_escalation defaults to False in KubeSpawner 2+. Since its a
- # property where None, False, and True all are valid values that users of the
- # Helm chart may want to set, we can't use the set_config_if_not_none helper
- # function as someone may want to override the default False value to None.
- #
- c.KubeSpawner.allow_privilege_escalation = get_config(
- "singleuser.allowPrivilegeEscalation"
- )
-
- # Combine imagePullSecret.create (single), imagePullSecrets (list), and
- # singleuser.image.pullSecrets (list).
- image_pull_secrets = []
- if get_config("imagePullSecret.automaticReferenceInjection") and get_config(
- "imagePullSecret.create"
- ):
- image_pull_secrets.append(get_name("image-pull-secret"))
- if get_config("imagePullSecrets"):
- image_pull_secrets.extend(get_config("imagePullSecrets"))
- if get_config("singleuser.image.pullSecrets"):
- image_pull_secrets.extend(get_config("singleuser.image.pullSecrets"))
- if image_pull_secrets:
- c.KubeSpawner.image_pull_secrets = image_pull_secrets
-
- # scheduling:
- if get_config("scheduling.userScheduler.enabled"):
- c.KubeSpawner.scheduler_name = get_name("user-scheduler")
- if get_config("scheduling.podPriority.enabled"):
- c.KubeSpawner.priority_class_name = get_name("priority")
-
- # add node-purpose affinity
- match_node_purpose = get_config("scheduling.userPods.nodeAffinity.matchNodePurpose")
- if match_node_purpose:
- node_selector = dict(
- matchExpressions=[
- dict(
- key="hub.jupyter.org/node-purpose",
- operator="In",
- values=["user"],
- )
- ],
- )
- if match_node_purpose == "prefer":
- c.KubeSpawner.node_affinity_preferred.append(
- dict(
- weight=100,
- preference=node_selector,
- ),
- )
- elif match_node_purpose == "require":
- c.KubeSpawner.node_affinity_required.append(node_selector)
- elif match_node_purpose == "ignore":
- pass
- else:
- raise ValueError(
- f"Unrecognized value for matchNodePurpose: {match_node_purpose}"
- )
-
- # Combine the common tolerations for user pods with singleuser tolerations
- scheduling_user_pods_tolerations = get_config("scheduling.userPods.tolerations", [])
- singleuser_extra_tolerations = get_config("singleuser.extraTolerations", [])
- tolerations = scheduling_user_pods_tolerations + singleuser_extra_tolerations
- if tolerations:
- c.KubeSpawner.tolerations = tolerations
-
- # Configure dynamically provisioning pvc
- storage_type = get_config("singleuser.storage.type")
- if storage_type == "dynamic":
- pvc_name_template = get_config("singleuser.storage.dynamic.pvcNameTemplate")
- c.KubeSpawner.pvc_name_template = pvc_name_template
- volume_name_template = get_config("singleuser.storage.dynamic.volumeNameTemplate")
- c.KubeSpawner.storage_pvc_ensure = True
- set_config_if_not_none(
- c.KubeSpawner, "storage_class", "singleuser.storage.dynamic.storageClass"
- )
- set_config_if_not_none(
- c.KubeSpawner,
- "storage_access_modes",
- "singleuser.storage.dynamic.storageAccessModes",
- )
- set_config_if_not_none(
- c.KubeSpawner, "storage_capacity", "singleuser.storage.capacity"
- )
-
- # Add volumes to singleuser pods
- c.KubeSpawner.volumes = [
- {
- "name": volume_name_template,
- "persistentVolumeClaim": {"claimName": pvc_name_template},
- }
- ]
- c.KubeSpawner.volume_mounts = [
- {
- "mountPath": get_config("singleuser.storage.homeMountPath"),
- "name": volume_name_template,
- }
- ]
- elif storage_type == "static":
- pvc_claim_name = get_config("singleuser.storage.static.pvcName")
- c.KubeSpawner.volumes = [
- {"name": "home", "persistentVolumeClaim": {"claimName": pvc_claim_name}}
- ]
-
- c.KubeSpawner.volume_mounts = [
- {
- "mountPath": get_config("singleuser.storage.homeMountPath"),
- "name": "home",
- "subPath": get_config("singleuser.storage.static.subPath"),
- }
- ]
-
- # Inject singleuser.extraFiles as volumes and volumeMounts with data loaded from
- # the dedicated k8s Secret prepared to hold the extraFiles actual content.
- extra_files = get_config("singleuser.extraFiles", {})
- if extra_files:
- volume = {
- "name": "files",
- }
- items = []
- for file_key, file_details in extra_files.items():
- # Each item is a mapping of a key in the k8s Secret to a path in this
- # abstract volume, the goal is to enable us to set the mode /
- # permissions only though so we don't change the mapping.
- item = {
- "key": file_key,
- "path": file_key,
- }
- if "mode" in file_details:
- item["mode"] = file_details["mode"]
- items.append(item)
- volume["secret"] = {
- "secretName": get_name("singleuser"),
- "items": items,
- }
- c.KubeSpawner.volumes.append(volume)
-
- volume_mounts = []
- for file_key, file_details in extra_files.items():
- volume_mounts.append(
- {
- "mountPath": file_details["mountPath"],
- "subPath": file_key,
- "name": "files",
- }
- )
- c.KubeSpawner.volume_mounts.extend(volume_mounts)
-
- # Inject extraVolumes / extraVolumeMounts
- c.KubeSpawner.volumes.extend(get_config("singleuser.storage.extraVolumes", []))
- c.KubeSpawner.volume_mounts.extend(
- get_config("singleuser.storage.extraVolumeMounts", [])
- )
-
- c.JupyterHub.services = []
- c.JupyterHub.load_roles = []
-
- # jupyterhub-idle-culler's permissions are scoped to what it needs only, see
- # https://github.com/jupyterhub/jupyterhub-idle-culler#permissions.
- #
- if get_config("cull.enabled", False):
- jupyterhub_idle_culler_role = {
- "name": "jupyterhub-idle-culler",
- "scopes": [
- "list:users",
- "read:users:activity",
- "read:servers",
- "delete:servers",
- # "admin:users", # dynamically added if --cull-users is passed
- ],
- # assign the role to a jupyterhub service, so it gains these permissions
- "services": ["jupyterhub-idle-culler"],
- }
-
- cull_cmd = ["python3", "-m", "jupyterhub_idle_culler"]
- base_url = c.JupyterHub.get("base_url", "/")
- cull_cmd.append("--url=http://localhost:8081" + url_path_join(base_url, "hub/api"))
-
- cull_timeout = get_config("cull.timeout")
- if cull_timeout:
- cull_cmd.append(f"--timeout={cull_timeout}")
-
- cull_every = get_config("cull.every")
- if cull_every:
- cull_cmd.append(f"--cull-every={cull_every}")
-
- cull_concurrency = get_config("cull.concurrency")
- if cull_concurrency:
- cull_cmd.append(f"--concurrency={cull_concurrency}")
-
- if get_config("cull.users"):
- cull_cmd.append("--cull-users")
- jupyterhub_idle_culler_role["scopes"].append("admin:users")
-
- if not get_config("cull.adminUsers"):
- cull_cmd.append("--cull-admin-users=false")
-
- if get_config("cull.removeNamedServers"):
- cull_cmd.append("--remove-named-servers")
-
- cull_max_age = get_config("cull.maxAge")
- if cull_max_age:
- cull_cmd.append(f"--max-age={cull_max_age}")
-
- c.JupyterHub.services.append(
- {
- "name": "jupyterhub-idle-culler",
- "command": cull_cmd,
- }
- )
- c.JupyterHub.load_roles.append(jupyterhub_idle_culler_role)
-
- for key, service in get_config("hub.services", {}).items():
- # c.JupyterHub.services is a list of dicts, but
- # hub.services is a dict of dicts to make the config mergable
- service.setdefault("name", key)
-
- # As the api_token could be exposed in hub.existingSecret, we need to read
- # it it from there or fall back to the chart managed k8s Secret's value.
- service.pop("apiToken", None)
- service["api_token"] = get_secret_value(f"hub.services.{key}.apiToken")
-
- c.JupyterHub.services.append(service)
-
- for key, role in get_config("hub.loadRoles", {}).items():
- # c.JupyterHub.load_roles is a list of dicts, but
- # hub.loadRoles is a dict of dicts to make the config mergable
- role.setdefault("name", key)
-
- c.JupyterHub.load_roles.append(role)
-
- # respect explicit null command (distinct from unspecified)
- # this avoids relying on KubeSpawner.cmd's default being None
- _unspecified = object()
- specified_cmd = get_config("singleuser.cmd", _unspecified)
- if specified_cmd is not _unspecified:
- c.Spawner.cmd = specified_cmd
-
- set_config_if_not_none(c.Spawner, "default_url", "singleuser.defaultUrl")
-
- cloud_metadata = get_config("singleuser.cloudMetadata", {})
-
- if cloud_metadata.get("blockWithIptables") == True:
- # Use iptables to block access to cloud metadata by default
- network_tools_image_name = get_config("singleuser.networkTools.image.name")
- network_tools_image_tag = get_config("singleuser.networkTools.image.tag")
- network_tools_resources = get_config("singleuser.networkTools.resources")
- ip_block_container = client.V1Container(
- name="block-cloud-metadata",
- image=f"{network_tools_image_name}:{network_tools_image_tag}",
- command=[
- "iptables",
- "-A",
- "OUTPUT",
- "-d",
- cloud_metadata.get("ip", "169.254.169.254"),
- "-j",
- "DROP",
- ],
- security_context=client.V1SecurityContext(
- privileged=True,
- run_as_user=0,
- capabilities=client.V1Capabilities(add=["NET_ADMIN"]),
- ),
- resources=network_tools_resources,
- )
-
- c.KubeSpawner.init_containers.append(ip_block_container)
-
-
- if get_config("debug.enabled", False):
- c.JupyterHub.log_level = "DEBUG"
- c.Spawner.debug = True
-
- # load potentially seeded secrets
- #
- # NOTE: ConfigurableHTTPProxy.auth_token is set through an environment variable
- # that is set using the chart managed secret.
- c.JupyterHub.cookie_secret = get_secret_value("hub.config.JupyterHub.cookie_secret")
- # NOTE: CryptKeeper.keys should be a list of strings, but we have encoded as a
- # single string joined with ; in the k8s Secret.
- #
- c.CryptKeeper.keys = get_secret_value("hub.config.CryptKeeper.keys").split(";")
-
- # load hub.config values, except potentially seeded secrets already loaded
- for app, cfg in get_config("hub.config", {}).items():
- if app == "JupyterHub":
- cfg.pop("proxy_auth_token", None)
- cfg.pop("cookie_secret", None)
- cfg.pop("services", None)
- elif app == "ConfigurableHTTPProxy":
- cfg.pop("auth_token", None)
- elif app == "CryptKeeper":
- cfg.pop("keys", None)
- c[app].update(cfg)
-
- # load /usr/local/etc/jupyterhub/jupyterhub_config.d config files
- config_dir = "/usr/local/etc/jupyterhub/jupyterhub_config.d"
- if os.path.isdir(config_dir):
- for file_path in sorted(glob.glob(f"{config_dir}/*.py")):
- file_name = os.path.basename(file_path)
- print(f"Loading {config_dir} config: {file_name}")
- with open(file_path) as f:
- file_content = f.read()
- # compiling makes debugging easier: https://stackoverflow.com/a/437857
- exec(compile(source=file_content, filename=file_name, mode="exec"))
-
- # execute hub.extraConfig entries
- for key, config_py in sorted(get_config("hub.extraConfig", {}).items()):
- print(f"Loading extra config: {key}")
- exec(config_py)
- z2jh.py: |
- """
- Utility methods for use in jupyterhub_config.py and dynamic subconfigs.
-
- Methods here can be imported by extraConfig in values.yaml
- """
- import os
- from collections.abc import Mapping
- from functools import lru_cache
-
- import yaml
-
-
- # memoize so we only load config once
- @lru_cache
- def _load_config():
- """Load the Helm chart configuration used to render the Helm templates of
- the chart from a mounted k8s Secret, and merge in values from an optionally
- mounted secret (hub.existingSecret)."""
-
- cfg = {}
- for source in ("secret/values.yaml", "existing-secret/values.yaml"):
- path = f"/usr/local/etc/jupyterhub/{source}"
- if os.path.exists(path):
- print(f"Loading {path}")
- with open(path) as f:
- values = yaml.safe_load(f)
- cfg = _merge_dictionaries(cfg, values)
- else:
- print(f"No config at {path}")
- return cfg
-
-
- @lru_cache
- def _get_config_value(key):
- """Load value from the k8s ConfigMap given a key."""
-
- path = f"/usr/local/etc/jupyterhub/config/{key}"
- if os.path.exists(path):
- with open(path) as f:
- return f.read()
- else:
- raise Exception(f"{path} not found!")
-
-
- @lru_cache
- def get_secret_value(key, default="never-explicitly-set"):
- """Load value from the user managed k8s Secret or the default k8s Secret
- given a key."""
-
- for source in ("existing-secret", "secret"):
- path = f"/usr/local/etc/jupyterhub/{source}/{key}"
- if os.path.exists(path):
- with open(path) as f:
- return f.read()
- if default != "never-explicitly-set":
- return default
- raise Exception(f"{key} not found in either k8s Secret!")
-
-
- def get_name(name):
- """Returns the fullname of a resource given its short name"""
- return _get_config_value(name)
-
-
- def get_name_env(name, suffix=""):
- """Returns the fullname of a resource given its short name along with a
- suffix, converted to uppercase with dashes replaced with underscores. This
- is useful to reference named services associated environment variables, such
- as PROXY_PUBLIC_SERVICE_PORT."""
- env_key = _get_config_value(name) + suffix
- env_key = env_key.upper().replace("-", "_")
- return os.environ[env_key]
-
-
- def _merge_dictionaries(a, b):
- """Merge two dictionaries recursively.
-
- Simplified From https://stackoverflow.com/a/7205107
- """
- merged = a.copy()
- for key in b:
- if key in a:
- if isinstance(a[key], Mapping) and isinstance(b[key], Mapping):
- merged[key] = _merge_dictionaries(a[key], b[key])
- else:
- merged[key] = b[key]
- else:
- merged[key] = b[key]
- return merged
-
-
- def get_config(key, default=None):
- """
- Find a config item of a given name & return it
-
- Parses everything as YAML, so lists and dicts are available too
-
- get_config("a.b.c") returns config['a']['b']['c']
- """
- value = _load_config()
- # resolve path in yaml
- for level in key.split("."):
- if not isinstance(value, dict):
- # a parent is a scalar or null,
- # can't resolve full path
- return default
- if level not in value:
- return default
- else:
- value = value[level]
- return value
-
-
- def set_config_if_not_none(cparent, name, key):
- """
- Find a config item of a given name, set the corresponding Jupyter
- configuration item if not None
- """
- data = get_config(key)
- if data is not None:
- setattr(cparent, name, data)
- checksum_hook-image-puller: "7bd6dc13e81a89aea6a4102dbd990324c34467773180d9be72c685039612ff37"
----
-# Source: jupyterhub/templates/scheduling/user-scheduler/configmap.yaml
-kind: ConfigMap
-apiVersion: v1
-metadata:
- name: user-scheduler
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-data:
- config.yaml: |
- # WARNING: The tag of this image is hardcoded, and the
- # "scheduling.userScheduler.plugins" configuration of the Helm
- # chart that generated this resource manifest wasn't respected. If
- # you install the Helm chart in a k8s cluster versioned 1.21 or
- # higher, your configuration will be respected.
- apiVersion: kubescheduler.config.k8s.io/v1beta1
- kind: KubeSchedulerConfiguration
- leaderElection:
- resourceLock: endpoints
- resourceName: user-scheduler-lock
- resourceNamespace: "default"
- profiles:
- - schedulerName: jupyterhub-user-scheduler
- plugins:
- score:
- disabled:
- - name: SelectorSpread
- - name: TaintToleration
- - name: PodTopologySpread
- - name: NodeResourcesBalancedAllocation
- - name: NodeResourcesLeastAllocated
- # Disable plugins to be allowed to enable them again with a
- # different weight and avoid an error.
- - name: NodePreferAvoidPods
- - name: NodeAffinity
- - name: InterPodAffinity
- - name: ImageLocality
- enabled:
- - name: NodePreferAvoidPods
- weight: 161051
- - name: NodeAffinity
- weight: 14631
- - name: InterPodAffinity
- weight: 1331
- - name: NodeResourcesMostAllocated
- weight: 121
- - name: ImageLocality
- weight: 11
----
-# Source: jupyterhub/templates/scheduling/user-scheduler/rbac.yaml
-kind: ClusterRole
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
- name: jupyterhub-user-scheduler
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-rules:
- # Copied from the system:kube-scheduler ClusterRole of the k8s version
- # matching the kube-scheduler binary we use. A modification has been made to
- # resourceName fields to remain relevant for how we have named our resources
- # in this Helm chart.
- #
- # NOTE: These rules have been:
- # - unchanged between 1.12 and 1.15
- # - changed in 1.16
- # - changed in 1.17
- # - unchanged between 1.18 and 1.20
- # - changed in 1.21: get/list/watch permission for namespace,
- # csidrivers, csistoragecapacities was added.
- # - unchanged between 1.22 and 1.23
- #
- # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L705-L861
- - apiGroups:
- - ""
- - events.k8s.io
- resources:
- - events
- verbs:
- - create
- - patch
- - update
- - apiGroups:
- - coordination.k8s.io
- resources:
- - leases
- verbs:
- - create
- - apiGroups:
- - coordination.k8s.io
- resourceNames:
- - user-scheduler-lock
- resources:
- - leases
- verbs:
- - get
- - update
- - apiGroups:
- - ""
- resources:
- - endpoints
- verbs:
- - create
- - apiGroups:
- - ""
- resourceNames:
- - user-scheduler-lock
- resources:
- - endpoints
- verbs:
- - get
- - update
- - apiGroups:
- - ""
- resources:
- - nodes
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - ""
- resources:
- - pods
- verbs:
- - delete
- - get
- - list
- - watch
- - apiGroups:
- - ""
- resources:
- - bindings
- - pods/binding
- verbs:
- - create
- - apiGroups:
- - ""
- resources:
- - pods/status
- verbs:
- - patch
- - update
- - apiGroups:
- - ""
- resources:
- - replicationcontrollers
- - services
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - apps
- - extensions
- resources:
- - replicasets
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - apps
- resources:
- - statefulsets
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - policy
- resources:
- - poddisruptionbudgets
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - ""
- resources:
- - persistentvolumeclaims
- - persistentvolumes
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - authentication.k8s.io
- resources:
- - tokenreviews
- verbs:
- - create
- - apiGroups:
- - authorization.k8s.io
- resources:
- - subjectaccessreviews
- verbs:
- - create
- - apiGroups:
- - storage.k8s.io
- resources:
- - csinodes
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - ""
- resources:
- - namespaces
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - storage.k8s.io
- resources:
- - csidrivers
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - storage.k8s.io
- resources:
- - csistoragecapacities
- verbs:
- - get
- - list
- - watch
- # Copied from the system:volume-scheduler ClusterRole of the k8s version
- # matching the kube-scheduler binary we use.
- #
- # NOTE: These rules have not changed between 1.12 and 1.23.
- #
- # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1280-L1307
- - apiGroups:
- - ""
- resources:
- - persistentvolumes
- verbs:
- - get
- - list
- - patch
- - update
- - watch
- - apiGroups:
- - storage.k8s.io
- resources:
- - storageclasses
- verbs:
- - get
- - list
- - watch
- - apiGroups:
- - ""
- resources:
- - persistentvolumeclaims
- verbs:
- - get
- - list
- - patch
- - update
- - watch
----
-# Source: jupyterhub/templates/scheduling/user-scheduler/rbac.yaml
-kind: ClusterRoleBinding
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
- name: jupyterhub-user-scheduler
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-subjects:
- - kind: ServiceAccount
- name: user-scheduler
- namespace: "default"
-roleRef:
- kind: ClusterRole
- name: jupyterhub-user-scheduler
- apiGroup: rbac.authorization.k8s.io
----
-# Source: jupyterhub/templates/hub/rbac.yaml
-kind: Role
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-rules:
- - apiGroups: [""] # "" indicates the core API group
- resources: ["pods", "persistentvolumeclaims", "secrets", "services"]
- verbs: ["get", "watch", "list", "create", "delete"]
- - apiGroups: [""] # "" indicates the core API group
- resources: ["events"]
- verbs: ["get", "watch", "list"]
----
-# Source: jupyterhub/templates/hub/rbac.yaml
-kind: RoleBinding
-apiVersion: rbac.authorization.k8s.io/v1
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-subjects:
- - kind: ServiceAccount
- name: hub
- namespace: "default"
-roleRef:
- kind: Role
- name: hub
- apiGroup: rbac.authorization.k8s.io
----
-# Source: jupyterhub/templates/hub/service.yaml
-apiVersion: v1
-kind: Service
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
- annotations:
- prometheus.io/scrape: "true"
- prometheus.io/path: /hub/metrics
- prometheus.io/port: "8081"
-spec:
- type: ClusterIP
- selector:
- component: hub
- app: jupyterhub
- release: jupyterhub
- ports:
- - name: hub
- port: 8081
- targetPort: http
----
-# Source: jupyterhub/templates/proxy/service.yaml
-apiVersion: v1
-kind: Service
-metadata:
- name: proxy-api
- labels:
- component: proxy-api
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- selector:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- ports:
- - port: 8001
- targetPort: api
----
-# Source: jupyterhub/templates/proxy/service.yaml
-apiVersion: v1
-kind: Service
-metadata:
- name: proxy-public
- labels:
- component: proxy-public
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- selector:
- component: proxy
- release: jupyterhub
- ports:
- - name: http
- port: 80
- targetPort: http
- type: LoadBalancer
----
-# Source: jupyterhub/templates/image-puller/daemonset-continuous.yaml
-apiVersion: apps/v1
-kind: DaemonSet
-metadata:
- name: continuous-image-puller
- labels:
- component: continuous-image-puller
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- selector:
- matchLabels:
- component: continuous-image-puller
- app: jupyterhub
- release: jupyterhub
- updateStrategy:
- type: RollingUpdate
- rollingUpdate:
- maxUnavailable: 100%
- template:
- metadata:
- labels:
- component: continuous-image-puller
- app: jupyterhub
- release: jupyterhub
- spec:
- tolerations:
- - effect: NoSchedule
- key: hub.jupyter.org/dedicated
- operator: Equal
- value: user
- - effect: NoSchedule
- key: hub.jupyter.org_dedicated
- operator: Equal
- value: user
- affinity:
- nodeAffinity:
- requiredDuringSchedulingIgnoredDuringExecution:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/arch
- operator: In
- values:
- - amd64
- - i386
- - i686
- - x86
- terminationGracePeriodSeconds: 0
- automountServiceAccountToken: false
- initContainers:
- - name: image-pull-metadata-block
- image: jupyterhub/k8s-network-tools:2.0.0
- command:
- - /bin/sh
- - -c
- - echo "Pulling complete"
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
- - name: image-pull-singleuser
- image: jupyter/scipy-notebook:82ce73789ba4
- command:
- - /bin/sh
- - -c
- - echo "Pulling complete"
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
- - name: image-pull-singleuser-profilelist-0
- image: jupyter/minimal-notebook:82ce73789ba4
- command:
- - /bin/sh
- - -c
- - echo "Pulling complete"
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
- - name: image-pull-singleuser-profilelist-2
- image: jupyter/all-spark-notebook:2343e33dec46
- command:
- - /bin/sh
- - -c
- - echo "Pulling complete"
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
- - name: image-pull-singleuser-profilelist-3
- image: jupyter/datascience-notebook:2343e33dec46
- command:
- - /bin/sh
- - -c
- - echo "Pulling complete"
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
- containers:
- - name: pause
- image: k8s.gcr.io/pause:3.8
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
----
-# Source: jupyterhub/templates/hub/deployment.yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: hub
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- replicas: 1
- selector:
- matchLabels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- strategy:
- type: Recreate
- template:
- metadata:
- labels:
- component: hub
- app: jupyterhub
- release: jupyterhub
- hub.jupyter.org/network-access-proxy-api: "true"
- hub.jupyter.org/network-access-proxy-http: "true"
- hub.jupyter.org/network-access-singleuser: "true"
- annotations:
- checksum/config-map: 6c42046cf2c7bb4966e23d2b4bca93b63e447960fd787b3f62174b39ad49cbac
- checksum/secret: 96beafcb491ebade4c38d0f6aaf749c93905ae5fc71440a45b79e13070c8a168
- spec:
- nodeSelector:
- kubernetes.io/arch: amd64
- tolerations:
- - effect: NoSchedule
- key: hub.jupyter.org/dedicated
- operator: Equal
- value: core
- - effect: NoSchedule
- key: hub.jupyter.org_dedicated
- operator: Equal
- value: core
- affinity:
- nodeAffinity:
- preferredDuringSchedulingIgnoredDuringExecution:
- - weight: 100
- preference:
- matchExpressions:
- - key: hub.jupyter.org/node-purpose
- operator: In
- values: [core]
- volumes:
- - name: config
- configMap:
- name: hub
- - name: secret
- secret:
- secretName: hub
- serviceAccountName: hub
- securityContext:
- fsGroup: 1000
- containers:
- - name: hub
- image: jupyterhub/k8s-hub:2.0.0
- args:
- - jupyterhub
- - --config
- - /usr/local/etc/jupyterhub/jupyterhub_config.py
- volumeMounts:
- - mountPath: /usr/local/etc/jupyterhub/jupyterhub_config.py
- subPath: jupyterhub_config.py
- name: config
- - mountPath: /usr/local/etc/jupyterhub/z2jh.py
- subPath: z2jh.py
- name: config
- - mountPath: /usr/local/etc/jupyterhub/config/
- name: config
- - mountPath: /usr/local/etc/jupyterhub/secret/
- name: secret
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 1000
- runAsUser: 1000
- env:
- - name: PYTHONUNBUFFERED
- value: "1"
- - name: HELM_RELEASE_NAME
- value: "jupyterhub"
- - name: POD_NAMESPACE
- valueFrom:
- fieldRef:
- fieldPath: metadata.namespace
- - name: CONFIGPROXY_AUTH_TOKEN
- valueFrom:
- secretKeyRef:
- name: hub
- key: hub.config.ConfigurableHTTPProxy.auth_token
- ports:
- - name: http
- containerPort: 8081
- livenessProbe:
- initialDelaySeconds: 300
- periodSeconds: 10
- timeoutSeconds: 3
- failureThreshold: 30
- httpGet:
- path: /hub/health
- port: http
- readinessProbe:
- initialDelaySeconds: 0
- periodSeconds: 2
- timeoutSeconds: 1
- failureThreshold: 1000
- httpGet:
- path: /hub/health
- port: http
----
-# Source: jupyterhub/templates/proxy/deployment.yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: proxy
- labels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- replicas: 1
- selector:
- matchLabels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- strategy:
- rollingUpdate: null
- type: Recreate
- template:
- metadata:
- labels:
- component: proxy
- app: jupyterhub
- release: jupyterhub
- hub.jupyter.org/network-access-hub: "true"
- hub.jupyter.org/network-access-singleuser: "true"
- annotations:
- # We want to restart proxy only if the auth token changes
- # Other changes to the hub config should not restart.
- # We truncate to 4 chars to avoid leaking auth token info,
- # since someone could brute force the hash to obtain the token
- #
- # Note that if auth_token has to be generated at random, it will be
- # generated at random here separately from being generated at random in
- # the k8s Secret template. This will cause this annotation to change to
- # match the k8s Secret during the first upgrade following an auth_token
- # was generated.
- checksum/auth-token: "dc85"
- checksum/proxy-secret: "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"
- spec:
- terminationGracePeriodSeconds: 60
- tolerations:
- - effect: NoSchedule
- key: hub.jupyter.org/dedicated
- operator: Equal
- value: core
- - effect: NoSchedule
- key: hub.jupyter.org_dedicated
- operator: Equal
- value: core
- affinity:
- nodeAffinity:
- preferredDuringSchedulingIgnoredDuringExecution:
- - weight: 100
- preference:
- matchExpressions:
- - key: hub.jupyter.org/node-purpose
- operator: In
- values: [core]
- containers:
- - name: chp
- image: jupyterhub/configurable-http-proxy:4.5.3
- command:
- - configurable-http-proxy
- - "--ip="
- - "--api-ip="
- - --api-port=8001
- - --default-target=http://hub:$(HUB_SERVICE_PORT)
- - --error-target=http://hub:$(HUB_SERVICE_PORT)/hub/error
- - --port=8000
- env:
- - name: CONFIGPROXY_AUTH_TOKEN
- valueFrom:
- secretKeyRef:
- # NOTE: References the chart managed k8s Secret even if
- # hub.existingSecret is specified to avoid using the
- # lookup function on the user managed k8s Secret.
- name: hub
- key: hub.config.ConfigurableHTTPProxy.auth_token
- ports:
- - name: http
- containerPort: 8000
- - name: api
- containerPort: 8001
- livenessProbe:
- initialDelaySeconds: 60
- periodSeconds: 10
- timeoutSeconds: 3
- failureThreshold: 30
- httpGet:
- path: /_chp_healthz
- port: http
- scheme: HTTP
- readinessProbe:
- initialDelaySeconds: 0
- periodSeconds: 2
- timeoutSeconds: 1
- failureThreshold: 1000
- httpGet:
- path: /_chp_healthz
- port: http
- scheme: HTTP
- securityContext:
- allowPrivilegeEscalation: false
- runAsGroup: 65534
- runAsUser: 65534
----
-# Source: jupyterhub/templates/scheduling/user-scheduler/deployment.yaml
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: user-scheduler
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- heritage: Helm
-spec:
- replicas: 2
- selector:
- matchLabels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- template:
- metadata:
- labels:
- component: user-scheduler
- app: jupyterhub
- release: jupyterhub
- annotations:
- checksum/config-map: 76e4e4553a55e750120e64d77985197755aa50907fc8f007a8348bc75694c64b
- spec:
- serviceAccountName: user-scheduler
- tolerations:
- - effect: NoSchedule
- key: hub.jupyter.org/dedica...*[Comment body truncated]*

@bloopy-boi
Copy link
Contributor

bloopy-boi bot commented Nov 7, 2024

🦙 MegaLinter status: ✅ SUCCESS

Descriptor Linter Files Fixed Errors Elapsed time
✅ COPYPASTE jscpd yes no 1.05s
✅ YAML prettier 1 0 0 0.44s
✅ YAML yamllint 1 0 0.28s

See detailed report in MegaLinter reports
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/cluster Changes made in the cluster directory renovate/helm size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. type/major
Projects
None yet
Development

Successfully merging this pull request may close these issues.

0 participants