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:
parent
923c8b3035
commit
f49b79fe40
1
.gitignore
vendored
1
.gitignore
vendored
@ -28,3 +28,4 @@ doc/build
|
|||||||
__pycache__
|
__pycache__
|
||||||
*.egg*
|
*.egg*
|
||||||
.stestr
|
.stestr
|
||||||
|
openrc
|
||||||
|
2
bindep.txt
Normal file
2
bindep.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
gcc [compile]
|
||||||
|
libc-dev [compile]
|
24
chart/crds/identity.openstack.org_services.yaml
Normal file
24
chart/crds/identity.openstack.org_services.yaml
Normal 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: []
|
@ -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:
|
||||||
|
@ -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 -}}
|
|
||||||
|
@ -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
2
chart/test-values.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
---
|
||||||
|
secretName: devstack
|
8
config/samples/identity_v1alpha1_services.yaml
Normal file
8
config/samples/identity_v1alpha1_services.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
apiVersion: identity.openstack.org/v1alpha1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: heat
|
||||||
|
spec:
|
||||||
|
type: orchestration
|
||||||
|
description: Heat Orchestration Service
|
31
devstack/override-defaults
Normal file
31
devstack/override-defaults
Normal 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
51
devstack/plugin.sh
Executable 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
20
devstack/settings
Normal 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
|
0
openstack_operator/openstack/__init__.py
Normal file
0
openstack_operator/openstack/__init__.py
Normal file
0
openstack_operator/openstack/identity/__init__.py
Normal file
0
openstack_operator/openstack/identity/__init__.py
Normal file
79
openstack_operator/openstack/identity/services.py
Normal file
79
openstack_operator/openstack/identity/services.py
Normal 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)
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -1,2 +1,3 @@
|
|||||||
kopf
|
kopf
|
||||||
Jinja2
|
Jinja2
|
||||||
|
openstacksdk
|
||||||
|
2
tox.ini
2
tox.ini
@ -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
|
||||||
|
@ -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]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user