Add zuul-registry deployment

This change adds an optional registry configuration to the spec:

  registry:
    image: docker.io/zuul/zuul-registry:latest
    count: 0
    storage-size: 20
    public-url: https://registry:9000

The operator expect a {{ cr_name }}-registry-tls secret to be provided
for tls and user configuration. If the secret is missing, the operator
creates self signed certificates and generates the user password.

Depends-On: https://review.opendev.org/710644
Change-Id: I0c054485b0ad01d53ddcff93f7bcbf34d1810325
This commit is contained in:
Tristan Cacqueray 2020-03-01 15:41:37 +00:00
parent 4a12041754
commit c6d35be4d6
10 changed files with 198 additions and 1 deletions

View File

@ -28,6 +28,7 @@
allowed-projects: zuul/zuul-operator
requires:
- zuul-container-image
- zuul-registry-container-image
- nodepool-container-image
provides:
- zuul-operator-container-image

View File

@ -83,6 +83,20 @@ let Schemas =
}
, default = { image = None Text, count = None Natural }
}
, Registry =
{ Type =
{ image : Optional Text
, count : Optional Natural
, storage-size : Optional Natural
, public-url : Optional Text
}
, default =
{ image = None Text
, count = None Natural
, storage-size = None Natural
, public-url = None Text
}
}
, Launcher =
{ Type = { image : Optional Text, config : UserSecret }
, default.image = None Text
@ -128,6 +142,7 @@ let Input =
, executor : Schemas.Executor.Type
, web : Schemas.Web.Type
, scheduler : Schemas.Scheduler.Type
, registry : Schemas.Registry.Type
, launcher : Schemas.Launcher.Type
, database : Optional UserSecret
, zookeeper : Optional UserSecret
@ -142,6 +157,7 @@ let Input =
, merger = Schemas.Merger.default
, web = Schemas.Web.default
, scheduler = Schemas.Scheduler.default
, registry = Schemas.Registry.default
, executor = Schemas.Executor.default
, launcher = Schemas.Launcher.default
, connections = Schemas.Connections.default

View File

@ -12,6 +12,14 @@ The resources expect secrets to be created by the zuul ansible role:
* `client.pem`
* `client.key`
* `${name}-registry-tls` with:
* `cert.pem`
* `cert.key`
* `secret` a password
* `username` the user name with write access
* `password` the user password
* `${name}-database-password` with a `password` key, (unless an input.database db uri is provided).
-}
let Prelude = ../Prelude.dhall
@ -72,6 +80,11 @@ let {- The Kubernetes resources of a Component
}
}
let DefaultNat =
\(value : Optional Natural)
-> \(default : Natural)
-> merge { None = default, Some = \(some : Natural) -> some } value
let DefaultText =
\(value : Optional Text)
-> \(default : Text)
@ -568,6 +581,38 @@ in \(input : Input)
[ { path = "zuul.conf", content = mkZuulConf input zk-hosts } ]
}
let etc-zuul-registry =
Volume::{
, name = input.name ++ "-secret-registry"
, dir = "/etc/zuul"
, files =
[ { path = "registry.yaml"
, content =
let public-url =
DefaultText
input.registry.public-url
"https://registry:9000"
in ''
registry:
address: '0.0.0.0'
port: 9000
public-url: ${public-url}
tls-cert: /etc/zuul-registry/cert.pem
tls-key: /etc/zuul-registry/cert.key
secret: "%(ZUUL_REGISTRY_secret)"
storage:
driver: filesystem
root: /var/lib/zuul
users:
- name: "%(ZUUL_REGISTRY_username)"
pass: "%(ZUUL_REGISTRY_password)"
access: write
''
}
]
}
let etc-nodepool =
Volume::{
, name = input.name ++ "-secret-nodepool"
@ -876,6 +921,72 @@ in \(input : Input)
}
)
}
, Registry =
let registry-volumes =
[ etc-zuul-registry
, Volume::{
, name = input.name ++ "-registry-tls"
, dir = "/etc/zuul-registry"
}
]
let registry-env =
mkEnvVarSecret
( Prelude.List.map
Text
EnvSecret
( \(key : Text)
-> { name = "ZUUL_REGISTRY_${key}"
, key = key
, secret =
input.name ++ "-registry-tls"
}
)
[ "secret", "username", "password" ]
)
in KubernetesComponent::{
, Service = Some
(mkService "registry" "registry" 9000)
, StatefulSet = Some
( mkStatefulSet
Component::{
, name = "registry"
, count =
DefaultNat input.registry.count 0
, data-dir = zuul-data-dir
, volumes = registry-volumes
, claim-size =
DefaultNat
input.registry.storage-size
20
, container = Kubernetes.Container::{
, name = "registry"
, image = zuul-image "registry"
, args = Some
[ "zuul-registry"
, "-c"
, "/etc/zuul/registry.yaml"
, "serve"
]
, imagePullPolicy = Some "IfNotPresent"
, ports = Some
[ Kubernetes.ContainerPort::{
, name = Some "registry"
, containerPort = 9000
}
]
, env = Some registry-env
, volumeMounts = Some
( mkVolumeMount
( registry-volumes
# zuul-data-dir
)
)
}
}
)
}
}
, Nodepool =
let nodepool-image =
@ -1008,13 +1119,17 @@ in \(input : Input)
{ apiVersion = "v1"
, kind = "List"
, items =
[ mkSecret etc-zuul, mkSecret etc-nodepool ]
[ mkSecret etc-zuul
, mkSecret etc-nodepool
, mkSecret etc-zuul-registry
]
# mkUnion Components.Backend.Database
# mkUnion Components.Backend.ZooKeeper
# mkUnion Components.Zuul.Scheduler
# mkUnion Components.Zuul.Executor
# mkUnion Components.Zuul.Web
# mkUnion Components.Zuul.Merger
# mkUnion Components.Zuul.Registry
# mkUnion Components.Nodepool.Launcher
}
}

View File

@ -12,6 +12,8 @@ spec:
scheduler:
config:
secretName: zuul-yaml-conf
registry:
count: 1
launcher:
config:
secretName: nodepool-yaml-conf

View File

@ -13,6 +13,8 @@ merger:
scheduler:
config:
secretName: zuul-yaml-conf
registry:
count: 0
launcher:
config:
secretName: nodepool-yaml-conf

View File

@ -5,6 +5,7 @@
command: python3 -m pip install --user openshift
roles:
- role: clear-firewall
- role: install-podman
- role: install-kubernetes
vars:
minikube_dns_resolvers:

View File

@ -132,6 +132,8 @@
kubernetes:
secretName: nodepool-kube-config
key: kube.config
registry:
count: 1
- name: Wait maximum 4 minutes for the scheduler deployment
shell: |
@ -164,3 +166,27 @@
- name: Wait an extra 2 minutes for the services to settle
pause:
minutes: 2
- name: Test the registry
block:
- name: Get registry service ip
command: kubectl get svc registry -o "jsonpath={.spec.clusterIP}"
register: _registry_ip
- name: Add registry to /etc/hosts
become: yes
lineinfile:
path: /etc/hosts
regexp: "^.* registry$"
line: "{{ _registry_ip.stdout_lines[0] }} registry"
- name: Get registry password
command: kubectl get secret zuul-registry-tls -o "jsonpath={.data.password}"
register: _registry_password
- name: Test registry login
command: >
podman login
--tls-verify=false registry:9000
-u zuul
-p "{{ _registry_password.stdout_lines[0] | b64decode }}"

View File

@ -0,0 +1,29 @@
- name: Check if registry tls cert exists
set_fact:
registry_certs: "{{ lookup('k8s', api_version='v1', kind='Secret', namespace=namespace, resource_name=zuul_name + '-registry-tls') }}"
- name: Generate and store certs
when: registry_certs.data is not defined
block:
- name: Generate certs
command: "{{ item }}"
loop:
# Server
- "openssl req -new -newkey rsa:2048 -nodes -keyout registry-{{ zuul_name }}.key -out registry-{{ zuul_name }}.csr -subj '/C=US/ST=Texas/L=Austin/O=Zuul/CN=server-{{ zuul_name }}'"
- "openssl x509 -req -days 3650 -in registry-{{ zuul_name }}.csr -out registry-{{ zuul_name }}.pem -CA ca-{{ zuul_name }}.pem -CAkey ca-{{ zuul_name }}.key -CAcreateserial"
- name: Create k8s secret
k8s:
state: "{{ state }}"
namespace: "{{ namespace }}"
definition:
apiVersion: v1
kind: Secret
metadata:
name: "{{ zuul_name }}-registry-tls"
stringData:
username: "zuul"
password: "{{ lookup('password', '/dev/null') }}"
secret: "{{ lookup('password', '/dev/null') }}"
cert.key: "{{ lookup('file', 'registry-' + zuul_name + '.key') }}"
cert.pem: "{{ lookup('file', 'registry-' + zuul_name + '.pem') }}"

View File

@ -10,4 +10,5 @@ raw_spec: "{{ vars['_operator_zuul-ci_org_zuul_spec'] | default(spec) }}"
# Provide sensible default for non optional attributes:
spec_defaults:
web: {}
registry: {}
externalConfig: {}

View File

@ -4,6 +4,10 @@
- zuul-lookup-conf
- zuul-ensure-gearman-tls
- include_role:
name: zuul-ensure-registry-tls
when: (raw_spec['registry']['count'] | default(0)) | int > 0
- include_role:
name: zuul-ensure-database-password
# when the user does not provide a db_uri