Add service CRD

This introduces a CRD for enabling/removing services from Keystone,
it also introduces a basic DevStack plugin framework so that we can
test things from it.  It also adds a framework for the operator to
setup SDK clients.

Change-Id: I183e560c6b32de2ce7adefeb1daa26def675bbe3
This commit is contained in:
Mohammed Naser 2020-04-25 09:06:57 -04:00
parent 923c8b3035
commit f49b79fe40
20 changed files with 284 additions and 5 deletions

1
.gitignore vendored
View File

@ -28,3 +28,4 @@ doc/build
__pycache__ __pycache__
*.egg* *.egg*
.stestr .stestr
openrc

2
bindep.txt Normal file
View File

@ -0,0 +1,2 @@
gcc [compile]
libc-dev [compile]

View File

@ -0,0 +1,24 @@
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: services.identity.openstack.org
spec:
group: identity.openstack.org
names:
kind: Service
listKind: ServiceList
plural: services
singular: service
scope: Cluster
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []

View File

@ -109,6 +109,26 @@ rules:
- get - get
- patch - patch
- update - update
- apiGroups:
- identity.openstack.org
resources:
- services
verbs:
- create
- delete
- get
- list
- patch
- update
- watch
- apiGroups:
- identity.openstack.org
resources:
- services/status
verbs:
- get
- patch
- update
- apiGroups: - apiGroups:
- infrastructure.vexxhost.cloud - infrastructure.vexxhost.cloud
resources: resources:

View File

@ -8,8 +8,6 @@
{{- end }} {{- end }}
{{- end -}} {{- end -}}
{{- if .Values.crd.dns }} {{- range $path, $bytes := .Files.Glob "crds/*.openstack.org_*.yaml" }}
{{- range $path, $bytes := .Files.Glob "crds/dns.openstack.org*.yaml" }}
{{ $.Files.Get $path }} {{ $.Files.Get $path }}
{{- end }} {{- end }}
{{- end -}}

View File

@ -21,6 +21,11 @@ spec:
- name: operator - name: operator
image: vexxhost/openstack-operator:latest image: vexxhost/openstack-operator:latest
command: ["/usr/local/bin/kopf"] command: ["/usr/local/bin/kopf"]
{{- with .Values.secretName }}
envFrom:
- secretRef:
name: {{ . }}
{{- end }}
args: args:
- run - run
- -m - -m
@ -28,6 +33,8 @@ spec:
- -m - -m
- openstack_operator.memcached - openstack_operator.memcached
- -m - -m
- openstack_operator.openstack.identity.services
- -m
- openstack_operator.rabbitmq - openstack_operator.rabbitmq
resources: resources:
limits: limits:

2
chart/test-values.yaml Normal file
View File

@ -0,0 +1,2 @@
---
secretName: devstack

View File

@ -0,0 +1,8 @@
---
apiVersion: identity.openstack.org/v1alpha1
kind: Service
metadata:
name: heat
spec:
type: orchestration
description: Heat Orchestration Service

View File

@ -0,0 +1,31 @@
#!/bin/bash
#
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
# Gets or creates service
# Usage: get_or_create_service <name> <type> <description>
function get_or_create_service {
cat <<EOF | kubectl apply -f-
---
apiVersion: identity.openstack.org/v1alpha1
kind: Service
metadata:
name: ${1//_/-}
spec:
type: $2
description: $3
EOF
}
export -f get_or_create_service

51
devstack/plugin.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/bash
#
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
function copy_minikube_config {
mkdir ~stack/.kube
sudo cp ~zuul/.kube/config ~stack/.kube/config
sudo cp ~zuul/.minikube/ca.crt ~stack/.kube/ca.crt
sudo cp ~zuul/.minikube/profiles/minikube/client.crt ~stack/.kube/client.crt
sudo cp ~zuul/.minikube/profiles/minikube/client.key ~stack/.kube/client.key
sudo chown -Rv stack:stack ~stack/.kube
sed -i s%/home/zuul/.minikube/profiles/minikube%/opt/stack/.kube% ~/.kube/config
sed -i s%/home/zuul/.minikube/ca.crt%/opt/stack/.kube/ca.crt% ~/.kube/config
kubectl cluster-info
}
if [[ "$1" == "stack" && "$2" == "pre-install" ]]; then
copy_minikube_config
elif [[ "$1" == "stack" && "$2" == "install" ]]; then
:
elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
:
elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
:
fi
if [[ "$1" == "unstack" ]]; then
:
fi
if [[ "$1" == "clean" ]]; then
:
fi

20
devstack/settings Normal file
View File

@ -0,0 +1,20 @@
#!/bin/bash
#
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
define_plugin openstack-operator
disable_service etcd3
disable_service rabbit

View File

View File

@ -0,0 +1,79 @@
# Copyright 2020 VEXXHOST, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Services Operator
This operator helps manage the creation and removal of services inside
Keystone using custom resources.
"""
import kopf
from openstack_operator import utils
def _get_service(conn, name, service_type):
"""Get a service from Keystone
This method will retrieve the service from Keystone, raise an error if it
found more than one or return None if it couldn't find it
"""
services = conn.search_services(name_or_id=name,
filters={"type": service_type})
if len(services) > 1:
raise RuntimeError("Found multiple services with name and type")
if len(services) == 0:
return None
return services[0]
@kopf.on.resume('identity.openstack.org', 'v1alpha1', 'services')
@kopf.on.create('identity.openstack.org', 'v1alpha1', 'services')
def create_or_resume(name, spec, **_):
"""Create or resume controller
This function runs when a new resource is created or when the controller
is first started. It creates or updates the appropriate service.
"""
conn = utils.get_openstack_connection()
service = _get_service(conn, name, spec["type"])
if service:
service = conn.update_service(service.id, name=name,
type=spec["type"],
description=spec["description"])
return
service = conn.create_service(name=name, type=spec["type"],
description=spec["description"])
@kopf.on.delete('identity.openstack.org', 'v1alpha1', 'services')
def delete(name, spec, **_):
"""Delete a service
This function runs when the servce CR is deleted and removes the record
from Keystone.
"""
conn = utils.get_openstack_connection()
service = _get_service(conn, name, spec["type"])
if not service:
return
conn.delete_service(service)

View File

@ -23,7 +23,9 @@ import operator
import os import os
import jinja2 import jinja2
import openstack
import kopf import kopf
from pbr import version
import pykube import pykube
import yaml import yaml
@ -31,6 +33,7 @@ from openstack_operator import objects
DIR_PATH = os.path.dirname(os.path.realpath(__file__)) DIR_PATH = os.path.dirname(os.path.realpath(__file__))
VERSION = version.VersionInfo('openstack_operator').version_string()
def to_yaml(value): def to_yaml(value):
@ -147,3 +150,9 @@ def get_ready_pod_ips(namespace, selector):
servers = sorted([p.obj["status"]["podIP"] for p in ready_pods]) servers = sorted([p.obj["status"]["podIP"] for p in ready_pods])
return servers return servers
def get_openstack_connection():
"""Get an instance of OpenStack SDK."""
return openstack.connect(cloud="envvars", app_name='openstack-operator',
app_version=VERSION)

View File

@ -55,6 +55,9 @@
name: write-devstack-local-conf name: write-devstack-local-conf
vars: vars:
devstack_localrc: "{{ _devstack_localrc | combine(_devstack_localrc_extra) }}" devstack_localrc: "{{ _devstack_localrc | combine(_devstack_localrc_extra) }}"
- name: Copy Zuul repo into devstack working directory
become: true
command: rsync -av src/opendev.org/vexxhost/openstack-operator /opt/stack
# Changes that run through devstack-tempest are likely to have an impact on # Changes that run through devstack-tempest are likely to have an impact on
# the devstack part of the job, so we keep devstack in the main play to # the devstack part of the job, so we keep devstack in the main play to

View File

@ -15,11 +15,30 @@
# limitations under the License. # limitations under the License.
- hosts: all - hosts: all
pre_tasks:
- name: Create secret for DevStack credentials
shell: |
cat <<EOF | kubectl apply -f-
apiVersion: v1
kind: Secret
metadata:
name: devstack
stringData:
OS_INSECURE: "true"
OS_AUTH_URL: http://{{ hostvars['controller']['nodepool']['private_ipv4'] }}/identity
OS_AUTH_TYPE: password
OS_PROJECT_DOMAIN_ID: default
OS_PROJECT_NAME: admin
OS_USER_DOMAIN_ID: default
OS_USERNAME: admin
OS_PASSWORD: secretadmin
EOF
roles: roles:
- role: helm-template - role: helm-template
vars: vars:
helm_release_name: openstack-operator helm_release_name: openstack-operator
helm_chart: ./chart helm_chart: ./chart
helm_values_file: ./chart/test-values.yaml
tasks: tasks:
# TODO(mnaser): Generate all manifests and ensure git is not dirty # TODO(mnaser): Generate all manifests and ensure git is not dirty
- include_tasks: tests/memcached.yaml - include_tasks: tests/memcached.yaml

View File

@ -1,2 +1,3 @@
kopf kopf
Jinja2 Jinja2
openstacksdk

View File

@ -4,6 +4,8 @@ envlist = py37
[testenv] [testenv]
usedevelop = True usedevelop = True
passenv =
OS_*
deps = deps =
-rtest-requirements.txt -rtest-requirements.txt
-rrequirements.txt -rrequirements.txt

View File

@ -10,6 +10,8 @@
devstack_services: devstack_services:
etcd3: false etcd3: false
rabbit: false rabbit: false
devstack_plugins:
openstack-operator: https://opendev.org/vexxhost/openstack-operator
docker_use_buildset_registry: true docker_use_buildset_registry: true
minikube_dns_resolvers: [1.1.1.1, 8.8.8.8] minikube_dns_resolvers: [1.1.1.1, 8.8.8.8]