Add quay-kubernetes package / Add quay CD to k8s

This commit is contained in:
Antoine Legrand 2017-07-17 01:24:31 +02:00
parent 2f750bfc87
commit 1d0313229e
45 changed files with 1129 additions and 10 deletions

View file

@ -0,0 +1 @@
stack/

View file

@ -0,0 +1,8 @@
{
name: 'quay/quay-app',
author: 'Antoine Legrand',
version: std.split(importstr "VERSION", "\n")[0],
description: 'quay',
license: 'MIT',
expander: 'jinja2'
}

View file

@ -0,0 +1,9 @@
# Configure
##
- The package reads automatically ./stack/* and use every files there in configuration.
- if a `./stack/config.yaml` exists, the file is merged with the variable `config`.
Config precedence order:
1. Default defined in the package level
2. file content in `stack/*`
3. value variables.stack_files
Except for `config.yaml`, precedence is a strict replacement, for config.yaml is a mergePatch.

View file

@ -0,0 +1,16 @@
local appr = import 'appr.libsonnet';
{
# Read all files in a directory
# @todo(ant31): replace walk by listdir
load_stack_files(path):: (
if appr.path_exists(path)
then {[appr.path.basename(file)]: appr.readfile(file, encode=true) for file in appr.walkdir(path)}
else {}
),
# Create a patch to add/update annotation with a rand value.
# Use to force a Deployment rolling-update
rand_label():: {metadata+: {annotations+: {'resource.appr/rand': appr.randAlphaNum()}}},
}

View file

@ -0,0 +1,155 @@
local appr = import 'appr.libsonnet';
local quaylib = import 'lib/quay.libsonnet';
function(
params={}
)
appr.package({
package: import "Chart.jsonnet",
variables: {
namespace: 'default',
cluster_domain_name: 'cluster.local',
# Minimum configuration
base_config: (import "templates/conf/config.libsonnet")($.variables),
# Additional values stack/config.yaml values
config: {},
# path to the local stack configuration directory
stack_path: "stack",
# load local `stack` directory if exists
stack_files: {
"syslog-ng-extra.conf": appr.b64encode(importstr "templates/conf/syslog-ng-extra.conf")} +
if $.variables.license != null then {"license": appr.b64encode($.variables.license) } else {} +
quaylib.load_stack_files($.variables.stack_path),
# load license
license: null,
# Image tag and repo
tag: $.package.version,
image: 'quay.io/quay/quay:%s' % self.tag,
# Used in the pull secret
docker_user: 'changeme',
docker_pass: 'changeme',
# Redis configuration
redis_host: 'quay-redis.%s.svc.%s:6379' % [$.variables.namespace, $.variables.cluster_domain_name],
redisconf: {redis_parts:: std.split($.variables.redis_host, ":"),
"host": self.redis_parts[0], port: self.redis_parts[1]},
# Configure the ingress with the ingress controller class and domain to use
domain: 'quay.%s.example.com' % $.variables.namespace,
ingress: {
class: 'nginx',
tls: "kubernetes.io/tls-acme",
domains: std.split($.variables.domain, ','),
annotations: {}
},
# Force to reload the secret/configuration
reconfigure: "false",
# Deploy a postgres (don't use it for prod)
deploy_db: 'false',
# Postgres deployment configuratio
db: {
user: 'quay',
password: 'quay',
name: 'quay',
},
# Quay DB_URI
db_uri: 'postgresql://%s:%s@postgres.%s.svc.%s/%s' % [$.variables.db.user,
$.variables.db.password,
$.variables.namespace,
$.variables.cluster_domain_name,
$.variables.db.name],
},
# ServiceAccount to attach Rbac rules
resources: appr.compact([ # + appr.importResourceDir('templates/')
{
value: {apiVersion: 'v1', kind: 'ServiceAccount',
metadata: {name: 'quay-enterprise'}}
},
# Grant secret read/write permission inside the namespace
{
value: (import 'templates/quay-enterprise-role.libsonnet')($.variables),
},
# Bind role to the Service account
{
value: (import 'templates/quay-enterprise-rolebinding.libsonnet')($.variables),
},
# Quay.io robot / user account. Protected from default values
{
value: (import 'templates/quay-enterprise-pullsecret.libsonnet')($.variables),
protected: if $.variables.docker_user == "changeme" || $.variables.docker_pass == "changeme"
then true else false
},
# Quay configuration files (quay/conf/stack), automatically read local the "./stack" directory to load values.
# Values can also be loaded from $.variables.stack_files
# Protected unless explicitly requested (reconfigure == "true").
{
value: (import 'templates/quay-enterprise-secret.libsonnet')($.variables),
protected: if $.variables.reconfigure == "true" then false else true
},
# Quay-registry deployment
# Force a rollout when the secret is reconfigured by updating a label (see randLabel)
{
value: appr.loadObject(appr.jinja2(importstr 'templates/quay-enterprise-app-dp.yaml', $.variables)) +
if $.variables.reconfigure == "true" then
# trigger a rollout
quaylib.rand_label()
else {}
}, # + {value+: if $.variables.reconfigure == "true" then randLabel() else {},},
{
template: (importstr 'templates/quay-enterprise-service.yaml'),
},
# Redis
{
template: (importstr 'templates/quay-enterprise-redis-service.yaml'),
},
{
template: (importstr 'templates/quay-enterprise-redis.yaml'),
},
# Ingress, assumes usage of kube-lego and an ingress controller.
# see variables.ingress for configuration
{
value: (import 'templates/quay-enterprise-ingress.libsonnet')($.variables.ingress),
},
]),
deploy: appr.compact([
if $.variables.deploy_db == 'true' then
{name: 'quay/postgres-app',
variables: {
user: $.variables.db.user,
dbname: $.variables.db.name,
password: $.variables.db.password
}},
{name: '$self'},
]),
}, params)

View file

@ -0,0 +1,53 @@
function(vars)
{
BUILDLOGS_REDIS: vars.redisconf,
USER_EVENTS_REDIS: vars.redisconf,
DB_URI: vars.db_uri,
SETUP_COMPLETE: true,
# Not deployed, features forced turn off
FEATURE_SECURITY_SCANNER: false,
FEATURE_BUILD_SUPPORT: false,
FEATURE_ACI_CONVERSION: false,
FEATURE_GITHUB_BUILD: false,
FEATURE_BITBUCKET_BUILD: false,
FEATURE_GITLAB_BUILD: false,
GITHUB_TRIGGER_CONFIG: null,
GITLAB_TRIGGER_KIND: {},
AUTHENTICATION_TYPE: "Database",
PREFERRED_URL_SCHEME: "https",
SERVER_HOSTNAME: vars.ingress.domains[0],
EXTERNAL_TLS_TERMINATION: true,
INSTANCE_SERVICE_KEY_KID_LOCATION: 'conf/quay.kid',
INSTANCE_SERVICE_KEY_LOCATION: 'conf/quay.pem',
}
# local s3_storage = { local_us: ["S3Storage",
# {storage_path: "",
# s3_access_key: vars.storage.s3.access_key,
# s3_secret_key: vars.storage.s3.secret_key,
# s3_bucket: vars.storage.s3.bucket},
# ]
# };
# extra: {
# REGISTRY_TITLE: "Quay (%s)" % vars.ingress.host,
# REGISTRY_TITLE_SHORT: "Quay (%s)" % vars.ingress.host,
# TESTING: true,
# DEBUGGING: true,
# USE_CDN: false,
# FEATURE_ANONYMOUS_ACCESS: true,
# FEATURE_MAILING: false,
# AUTHENTICATION_TYPE: "Database",
# ENTERPRISE_LOGO_URL: "/static/img/quay-logo.png",
# LOG_ARCHIVE_LOCATION: "default",
# TAG_EXPIRATION_OPTIONS: ["2d"],
# DISTRIBUTED_STORAGE_CONFIG: {
# default: [
# "LocalStorage",
# {storage_path: "/datastorage/registry"}]},
# DISTRIBUTED_STORAGE_DEFAULT_LOCATIONS: [],
# DISTRIBUTED_STORAGE_PREFERENCE: ["default"],
# USERFILES_LOCATION: "default",
# USERFILES_PATH: "userfiles/",
# }
# }

View file

@ -0,0 +1,7 @@
destination d_stdout {
pipe("/dev/stdout");
};
log {
source(s_src); destination(d_stdout);
};

View file

@ -0,0 +1,37 @@
---
apiVersion: batch/v1
kind: Job
metadata:
name: quay-dev-initdb
spec:
activeDeadlineSeconds: 100
template:
metadata:
name: quay-dev-initdb
spec:
containers:
- name: quay
image: quay.io/quay/quay-ci:master
env:
- name: TEST_DATABASE_URI
value: "postgres://"
- name: SKIP_DB_SCHEMA
value: "true"
command:
- venv/bin/python
- initdb.py
volumeMounts:
- name: configvolume
readOnly: false
mountPath: /conf/stack
resources:
limits:
cpu: 500m
memory: 500Mi
imagePullSecrets:
- name: coreos-pull-secret
volumes:
- name: configvolume
secret:
secretName: quay-enterprise-config-secret
restartPolicy: Never

View file

@ -0,0 +1,55 @@
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
namespace: quay-enterprise
name: quay-enterprise-app
labels:
quay-enterprise-component: app
spec:
replicas: 1
template:
metadata:
labels:
quay-enterprise-component: app
spec:
serviceAccountName: "quay-enterprise"
containers:
- name: quay-enterprise-app
livenessProbe:
httpGet:
path: /status
port: 80
initialDelaySeconds: 300
periodSeconds: 30
failureThreshold: 3
successThreshold: 1
readinessProbe:
httpGet:
path: /status
port: 80
initialDelaySeconds: 45
failureThreshold: 6
periodSeconds: 20
env:
- name: QE_K8S_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
image: {{image}}
ports:
- containerPort: 80
volumeMounts:
- name: configvolume
readOnly: false
mountPath: /conf/stack
resources:
limits:
cpu: 1
memory: 2Gi
imagePullSecrets:
- name: coreos-pull-secret
volumes:
- name: configvolume
secret:
secretName: quay-enterprise-config-secret

View file

@ -0,0 +1,31 @@
function(ingress={ class: 'none', tls: "true", domains: ['quay.example.com'] })
{
apiVersion: "extensions/v1beta1",
kind: "Ingress",
metadata: {
annotations: {
"kubernetes.io/ingress.class": ingress.class,
[if std.type(ingress.tls) == "string" then ingress.tls]: "true"} +
if std.objectHas(ingress, "annotations") then ingress.annotations else {},
name: "quay-enterprise",
},
spec: {
rules: [{
host: domain,
http: {
paths: [{
backend: {
serviceName: "quay-enterprise",
servicePort: 80,},
path: "/"}]},
} for domain in ingress.domains],
} +
if std.type(ingress.tls) == "string" then
{tls: [{
hosts: ingress.domains,
secretName: "quay-enterprise-tls",
}]} else {},
}

View file

@ -0,0 +1,14 @@
local appr = import "appr.libsonnet";
function(variables={})
{
local docker_login = [variables.docker_user, variables.docker_pass],
data: {
".dockercfg": appr.b64encode('{"quay.io": {"username": "%s", "password": "%s","email":"toto@toto.com","auth": "%s"}}' % (docker_login + [appr.b64encode("%s:%s" % docker_login)])),
},
kind: "Secret",
metadata: {
name: "coreos-pull-secret",
},
type: "kubernetes.io/dockercfg"
}

View file

@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
namespace: quay-redis
name: quay-redis
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 6379
targetPort: 6379
selector:
quay-enterprise-component: redis

View file

@ -0,0 +1,24 @@
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
namespace: quay-enterprise
name: quay-enterprise-redis
labels:
quay-enterprise-component: redis
spec:
replicas: 1
template:
metadata:
labels:
quay-enterprise-component: redis
spec:
containers:
- name: redis-master
image: quay.io/quay/redis
ports:
- containerPort: 6379
resources:
limits:
cpu: 100m
memory: 500Mi

View file

@ -0,0 +1,21 @@
local appr = import "appr.libsonnet";
function(vars={})
{
kind: "Role",
apiVersion: 'rbac.authorization.k8s.io/v1beta1',
metadata: {
name: "quay-enterprise",
},
rules: [
{
apiGroups: [""],
resources: ["secrets"],
verbs: ["get", "update", "patch"],
},
{
apiGroups: [""],
resources: ["namespaces"],
verbs: ["get"],
},
],
}

View file

@ -0,0 +1,21 @@
function(vars={})
{
apiVersion: "rbac.authorization.k8s.io/v1beta1",
kind: "RoleBinding",
metadata: {
name: "quay-enterprise-binding",
namespace: vars.namespace,
},
roleRef: {
apiGroup: "rbac.authorization.k8s.io",
kind: "Role",
name: "quay-enterprise",
},
subjects: [
{
kind: "ServiceAccount",
name: "quay-enterprise",
namespace: vars.namespace,
}
]
}

View file

@ -0,0 +1,34 @@
local appr = import "appr.libsonnet";
local b64e = appr.b64decode;
function(vars={})
# Deserialize config.yaml if exists
local local_stack_config = (
local confpath = "config.yaml";
if std.objectHasAll(vars.stack_files, confpath)
then appr.loadObject(appr.b64decode(vars.stack_files[confpath]))
else {}
);
# Merge all config together
# Precedence: package-config (vars.config) < local stack/config.yaml < base-config (vars.base-config)
local config_yaml = {'config.yaml': appr.b64encode(appr.to_yaml(
vars.config +
local_stack_config +
vars.base_config))};
# Merge stack files
local stack_files = vars.stack_files + config_yaml;
{
apiVersion: "v1",
kind: "Secret",
metadata: {
namespace: "quay-enterprise",
name: "quay-enterprise-config-secret"},
# base64 encode all files
data: { [file]: stack_files[file]
for file in std.objectFields(stack_files) if stack_files[file] != null}
}

View file

@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
namespace: quay-enterprise
name: quay-enterprise
spec:
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
selector:
quay-enterprise-component: app