Support octavia-ingress-controller

- Add "octavia" as one of the "ingress_controller" options.
- Add label "octavia_ingress_controller_tag".
- Use external network ID in the heat templates.

Story: 2004838
Change-Id: I7d889a054cd5feb2eeef523b20607a6c7630d777
This commit is contained in:
Lingxian Kong 2019-01-17 01:38:44 +13:00
parent f63761a804
commit a941822c8e
13 changed files with 492 additions and 20 deletions

View File

@ -347,6 +347,8 @@ the table are linked to more details elsewhere in the user guide.
+---------------------------------------+--------------------+---------------+ +---------------------------------------+--------------------+---------------+
| `ingress_controller_role`_ | see below | "ingress" | | `ingress_controller_role`_ | see below | "ingress" |
+---------------------------------------+--------------------+---------------+ +---------------------------------------+--------------------+---------------+
| `octavia_ingress_controller_tag`_ | see below | see below |
+---------------------------------------+--------------------+---------------+
| `kubelet_options`_ | extra kubelet args | "" | | `kubelet_options`_ | extra kubelet args | "" |
+---------------------------------------+--------------------+---------------+ +---------------------------------------+--------------------+---------------+
| `kubeapi_options`_ | extra kubeapi args | "" | | `kubeapi_options`_ | extra kubeapi args | "" |
@ -1235,8 +1237,11 @@ Magnum allows selecting one of multiple controller options via the
your own Ingress resources. your own Ingress resources.
_`ingress_controller` _`ingress_controller`
This label sets the Ingress Controller to be used. Currently only traefik This label sets the Ingress Controller to be used. Currently 'traefik' and
is supported. The default is '', meaning no Ingress Controller configured. 'octavia' are supported. The default is '', meaning no Ingress Controller
configured. For more details about octavia-ingress-controller please refer
to `cloud-provider-openstack document
<https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/using-octavia-ingress-controller.md>`_
_`ingress_controller_role` _`ingress_controller_role`
This label defines the role nodes should have to run an instance of the This label defines the role nodes should have to run an instance of the
@ -1247,6 +1252,11 @@ _`ingress_controller_role`
kubectl label node <node-name> role=ingress kubectl label node <node-name> role=ingress
This lable is not used for octavia-ingress-controller.
_`octavia_ingress_controller_tag`
The image tag for octavia-ingress-controller. Stain-default: 1.13.2-alpha
DNS DNS
--- ---

View File

@ -53,3 +53,25 @@ def delete_floatingip(context, fix_port_id, cluster):
except Exception as e: except Exception as e:
raise exception.PreDeletionFailed(cluster_uuid=cluster.uuid, raise exception.PreDeletionFailed(cluster_uuid=cluster.uuid,
msg=str(e)) msg=str(e))
def get_network_id(context, network_name):
nets = []
n_client = clients.OpenStackClients(context).neutron()
ext_filter = {'router:external': True}
networks = n_client.list_networks(**ext_filter)
for net in networks.get('networks'):
if net.get('name') == network_name:
nets.append(net)
if len(nets) == 0:
raise exception.ExternalNetworkNotFound(network=network_name)
if len(nets) > 1:
raise exception.Conflict(
"Multiple networks exist with same name '%s'. Please use the "
"network ID instead." % network_name
)
return nets[0]["id"]

View File

@ -3,9 +3,6 @@
step="enable-ingress-controller" step="enable-ingress-controller"
printf "Starting to run ${step}\n" printf "Starting to run ${step}\n"
# Enables the specified ingress controller.
#
# Currently there is only support for traefik.
. /etc/sysconfig/heat-params . /etc/sysconfig/heat-params
function writeFile { function writeFile {
@ -21,8 +18,20 @@ EOF
} }
} }
if [ "$(echo $INGRESS_CONTROLLER | tr '[:upper:]' '[:lower:]')" = "traefik" ]; then ingress_controller=$(echo $INGRESS_CONTROLLER | tr '[:upper:]' '[:lower:]')
case "$ingress_controller" in
"")
echo "No ingress controller configured."
;;
"traefik")
$enable-ingress-traefik $enable-ingress-traefik
fi ;;
"octavia")
$enable-ingress-octavia
;;
*)
echo "Ingress controller $ingress_controller not supported."
;;
esac
printf "Finished running ${step}\n" printf "Finished running ${step}\n"

View File

@ -0,0 +1,122 @@
# octavia-ingress-controller RBAC
OCTAVIA_INGRESS_CONTROLLER_RBAC=/srv/magnum/kubernetes/manifests/octavia-ingress-controller-rbac.yaml
OCTAVIA_INGRESS_CONTROLLER_RBAC_CONTENT=$(cat <<EOF
---
kind: ServiceAccount
apiVersion: v1
metadata:
name: octavia-ingress-controller
namespace: kube-system
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: octavia-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: octavia-ingress-controller
namespace: kube-system
EOF
)
writeFile $OCTAVIA_INGRESS_CONTROLLER_RBAC "$OCTAVIA_INGRESS_CONTROLLER_RBAC_CONTENT"
# octavia-ingress-controller config file
OCTAVIA_INGRESS_CONTROLLER_CONFIGMAP=/srv/magnum/kubernetes/manifests/octavia-ingress-controller-config.yaml
OCTAVIA_INGRESS_CONTROLLER_CONFIGMAP_CONTENT=$(cat <<EOF
---
kind: ConfigMap
apiVersion: v1
metadata:
name: octavia-ingress-controller-config
namespace: kube-system
data:
config: |
cluster_name: ${CLUSTER_UUID}
openstack:
auth_url: ${AUTH_URL}
user_id: ${TRUSTEE_USER_ID}
password: ${TRUSTEE_PASSWORD}
trust_id: ${TRUST_ID}
region: ${REGION_NAME}
ca_file: /etc/kubernetes/ca-bundle.crt
octavia:
subnet_id: ${CLUSTER_SUBNET}
floating_network_id: ${EXTERNAL_NETWORK_ID}
EOF
)
writeFile $OCTAVIA_INGRESS_CONTROLLER_CONFIGMAP "$OCTAVIA_INGRESS_CONTROLLER_CONFIGMAP_CONTENT"
# octavia-ingress-controller deployment
oic_image="${CONTAINER_INFRA_PREFIX:-docker.io/k8scloudprovider/}octavia-ingress-controller:${OCTAVIA_INGRESS_CONTROLLER_TAG}"
OCTAVIA_INGRESS_CONTROLLER=/srv/magnum/kubernetes/manifests/octavia-ingress-controller.yaml
OCTAVIA_INGRESS_CONTROLLER_CONTENT=$(cat <<EOF
---
kind: StatefulSet
apiVersion: apps/v1
metadata:
name: octavia-ingress-controller
namespace: kube-system
labels:
k8s-app: octavia-ingress-controller
spec:
replicas: 1
selector:
matchLabels:
k8s-app: octavia-ingress-controller
template:
metadata:
labels:
k8s-app: octavia-ingress-controller
spec:
serviceAccountName: octavia-ingress-controller
tolerations:
- effect: NoSchedule # Make sure the pod can be scheduled on master kubelet.
operator: Exists
- key: CriticalAddonsOnly # Mark the pod as a critical add-on for rescheduling.
operator: Exists
- effect: NoExecute
operator: Exists
nodeSelector:
node-role.kubernetes.io/master: "" # octavia-ingress-controller needs to access /etc/kubernetes folder.
containers:
- name: octavia-ingress-controller
image: ${oic_image}
imagePullPolicy: IfNotPresent
args:
- /bin/octavia-ingress-controller
- --config=/etc/config/octavia-ingress-controller-config.yaml
volumeMounts:
- mountPath: /etc/kubernetes
name: kubernetes-config
readOnly: true
- name: ingress-config
mountPath: /etc/config
hostNetwork: true
volumes:
- name: kubernetes-config
hostPath:
path: /etc/kubernetes
type: Directory
- name: ingress-config
configMap:
name: octavia-ingress-controller-config
items:
- key: config
path: octavia-ingress-controller-config.yaml
EOF
)
writeFile $OCTAVIA_INGRESS_CONTROLLER "$OCTAVIA_INGRESS_CONTROLLER_CONTENT"
echo "Waiting for Kubernetes API..."
until [ "ok" = "$(curl --silent http://127.0.0.1:8080/healthz)" ]
do
sleep 5
done
kubectl apply --validate=false -f $OCTAVIA_INGRESS_CONTROLLER_RBAC
kubectl apply --validate=false -f $OCTAVIA_INGRESS_CONTROLLER_CONFIGMAP
kubectl apply --validate=false -f $OCTAVIA_INGRESS_CONTROLLER

View File

@ -67,6 +67,7 @@ write_files:
CALICO_IPV4POOL="$CALICO_IPV4POOL" CALICO_IPV4POOL="$CALICO_IPV4POOL"
INGRESS_CONTROLLER="$INGRESS_CONTROLLER" INGRESS_CONTROLLER="$INGRESS_CONTROLLER"
INGRESS_CONTROLLER_ROLE="$INGRESS_CONTROLLER_ROLE" INGRESS_CONTROLLER_ROLE="$INGRESS_CONTROLLER_ROLE"
OCTAVIA_INGRESS_CONTROLLER_TAG="$OCTAVIA_INGRESS_CONTROLLER_TAG"
KUBELET_OPTIONS="$KUBELET_OPTIONS" KUBELET_OPTIONS="$KUBELET_OPTIONS"
KUBECONTROLLER_OPTIONS="$KUBECONTROLLER_OPTIONS" KUBECONTROLLER_OPTIONS="$KUBECONTROLLER_OPTIONS"
KUBEAPI_OPTIONS="$KUBEAPI_OPTIONS" KUBEAPI_OPTIONS="$KUBEAPI_OPTIONS"
@ -81,3 +82,4 @@ write_files:
KEYSTONE_AUTH_ENABLED="$KEYSTONE_AUTH_ENABLED" KEYSTONE_AUTH_ENABLED="$KEYSTONE_AUTH_ENABLED"
K8S_KEYSTONE_AUTH_TAG="$K8S_KEYSTONE_AUTH_TAG" K8S_KEYSTONE_AUTH_TAG="$K8S_KEYSTONE_AUTH_TAG"
PROJECT_ID="$PROJECT_ID" PROJECT_ID="$PROJECT_ID"
EXTERNAL_NETWORK_ID="$EXTERNAL_NETWORK_ID"

View File

@ -23,6 +23,7 @@ ca-file=/etc/kubernetes/ca-bundle.crt
[LoadBalancer] [LoadBalancer]
use-octavia=$OCTAVIA_ENABLED use-octavia=$OCTAVIA_ENABLED
subnet-id=$CLUSTER_SUBNET subnet-id=$CLUSTER_SUBNET
floating-network-id=$EXTERNAL_NETWORK_ID
create-monitor=yes create-monitor=yes
monitor-delay=1m monitor-delay=1m
monitor-timeout=30s monitor-timeout=30s

View File

@ -11,8 +11,11 @@
# under the License. # under the License.
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import uuidutils
from magnum.common import exception
from magnum.common import keystone from magnum.common import keystone
from magnum.common import neutron
from magnum.drivers.heat import template_def from magnum.drivers.heat import template_def
CONF = cfg.CONF CONF = cfg.CONF
@ -104,6 +107,17 @@ class K8sTemplateDefinition(template_def.BaseTemplateDefinition):
extra_params['octavia_enabled'] = keystone.is_octavia_enabled() extra_params['octavia_enabled'] = keystone.is_octavia_enabled()
# NOTE(lxkong): Convert external network name to UUID, the template
# field name is confused. If external_network_id is not specified in
# cluster template use 'public' as the default value, which is the same
# with the heat template default value as before.
ext_net = cluster_template.external_network_id or "public"
if not uuidutils.is_uuid_like(ext_net):
ext_net_id = neutron.get_network_id(context, ext_net)
extra_params['external_network'] = ext_net_id
else:
extra_params['external_network'] = ext_net
label_list = ['flannel_network_cidr', 'flannel_backend', label_list = ['flannel_network_cidr', 'flannel_backend',
'flannel_network_subnetlen', 'flannel_network_subnetlen',
'system_pods_initial_delay', 'system_pods_initial_delay',
@ -114,8 +128,8 @@ class K8sTemplateDefinition(template_def.BaseTemplateDefinition):
'kube_dashboard_enabled', 'kube_dashboard_enabled',
'etcd_volume_size', 'etcd_volume_size',
'cert_manager_api', 'cert_manager_api',
'ingress_controller',
'ingress_controller_role', 'ingress_controller_role',
'octavia_ingress_controller_tag',
'kubelet_options', 'kubelet_options',
'kubeapi_options', 'kubeapi_options',
'kubeproxy_options', 'kubeproxy_options',
@ -126,6 +140,15 @@ class K8sTemplateDefinition(template_def.BaseTemplateDefinition):
for label in label_list: for label in label_list:
extra_params[label] = cluster.labels.get(label) extra_params[label] = cluster.labels.get(label)
ingress_controller = cluster.labels.get('ingress_controller',
'').lower()
if (ingress_controller == 'octavia'
and not extra_params['octavia_enabled']):
raise exception.InvalidParameterValue(
'Octavia service needs to be deployed for octavia ingress '
'controller.')
extra_params["ingress_controller"] = ingress_controller
cluser_ip_range = cluster.labels.get('service_cluster_ip_range') cluser_ip_range = cluster.labels.get('service_cluster_ip_range')
if cluser_ip_range: if cluser_ip_range:
extra_params['portal_network_cidr'] = cluser_ip_range extra_params['portal_network_cidr'] = cluser_ip_range

View File

@ -14,8 +14,7 @@ parameters:
external_network: external_network:
type: string type: string
description: uuid/name of a network to use for floating ip addresses description: uuid of a network to use for floating ip addresses
default: public
fixed_network: fixed_network:
type: string type: string
@ -454,6 +453,11 @@ parameters:
node role where the ingress controller backend should run node role where the ingress controller backend should run
default: "ingress" default: "ingress"
octavia_ingress_controller_tag:
type: string
description: Octavia ingress controller docker image tag.
default: "1.13.2-alpha"
kubelet_options: kubelet_options:
type: string type: string
description: > description: >
@ -754,6 +758,7 @@ resources:
pods_network_cidr: {get_param: pods_network_cidr} pods_network_cidr: {get_param: pods_network_cidr}
ingress_controller: {get_param: ingress_controller} ingress_controller: {get_param: ingress_controller}
ingress_controller_role: {get_param: ingress_controller_role} ingress_controller_role: {get_param: ingress_controller_role}
octavia_ingress_controller_tag: {get_param: octavia_ingress_controller_tag}
kubelet_options: {get_param: kubelet_options} kubelet_options: {get_param: kubelet_options}
kubeapi_options: {get_param: kubeapi_options} kubeapi_options: {get_param: kubeapi_options}
kubeproxy_options: {get_param: kubeproxy_options} kubeproxy_options: {get_param: kubeproxy_options}
@ -791,6 +796,7 @@ resources:
- str_replace: - str_replace:
params: params:
$enable-ingress-traefik: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-traefik.sh} $enable-ingress-traefik: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-traefik.sh}
$enable-ingress-octavia: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-octavia.sh}
template: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-controller.sh} template: {get_file: ../../common/templates/kubernetes/fragments/enable-ingress-controller.sh}
- get_file: ../../common/templates/kubernetes/fragments/kube-dashboard-service.sh - get_file: ../../common/templates/kubernetes/fragments/kube-dashboard-service.sh
- get_file: ../../common/templates/kubernetes/fragments/enable-keystone-auth.sh - get_file: ../../common/templates/kubernetes/fragments/enable-keystone-auth.sh

View File

@ -25,7 +25,7 @@ parameters:
external_network: external_network:
type: string type: string
description: uuid/name of a network to use for floating ip addresses description: uuid of a network to use for floating ip addresses
portal_network_cidr: portal_network_cidr:
type: string type: string
@ -341,6 +341,10 @@ parameters:
description: > description: >
node role where the ingress controller should run node role where the ingress controller should run
octavia_ingress_controller_tag:
type: string
description: Octavia ingress controller docker image tag.
kubelet_options: kubelet_options:
type: string type: string
description: > description: >
@ -504,6 +508,7 @@ resources:
"$CALICO_IPV4POOL": {get_param: calico_ipv4pool} "$CALICO_IPV4POOL": {get_param: calico_ipv4pool}
"$INGRESS_CONTROLLER": {get_param: ingress_controller} "$INGRESS_CONTROLLER": {get_param: ingress_controller}
"$INGRESS_CONTROLLER_ROLE": {get_param: ingress_controller_role} "$INGRESS_CONTROLLER_ROLE": {get_param: ingress_controller_role}
"$OCTAVIA_INGRESS_CONTROLLER_TAG": {get_param: octavia_ingress_controller_tag}
"$KUBELET_OPTIONS": {get_param: kubelet_options} "$KUBELET_OPTIONS": {get_param: kubelet_options}
"$KUBEAPI_OPTIONS": {get_param: kubeapi_options} "$KUBEAPI_OPTIONS": {get_param: kubeapi_options}
"$KUBECONTROLLER_OPTIONS": {get_param: kubecontroller_options} "$KUBECONTROLLER_OPTIONS": {get_param: kubecontroller_options}
@ -518,6 +523,7 @@ resources:
"$KEYSTONE_AUTH_ENABLED": {get_param: keystone_auth_enabled} "$KEYSTONE_AUTH_ENABLED": {get_param: keystone_auth_enabled}
"$K8S_KEYSTONE_AUTH_TAG": {get_param: k8s_keystone_auth_tag} "$K8S_KEYSTONE_AUTH_TAG": {get_param: k8s_keystone_auth_tag}
"$PROJECT_ID": {get_param: project_id} "$PROJECT_ID": {get_param: project_id}
"$EXTERNAL_NETWORK_ID": {get_param: external_network}
install_openstack_ca: install_openstack_ca:
type: OS::Heat::SoftwareConfig type: OS::Heat::SoftwareConfig

View File

@ -136,3 +136,84 @@ class NeutronTest(base.TestCase):
fake_port_id, fake_port_id,
self.cluster self.cluster
) )
@mock.patch('magnum.common.clients.OpenStackClients')
def test_get_network_id(self, mock_clients):
fake_name = "fake_network"
fake_id = "24fe5da0-1ac0-11e9-84cd-00224d6b7bc1"
mock_nclient = mock.MagicMock()
mock_nclient.list_networks.return_value = {
'networks': [
{
'id': fake_id,
'name': fake_name,
'router:external': True
}
]
}
osc = mock.MagicMock()
mock_clients.return_value = osc
osc.neutron.return_value = mock_nclient
network_id = neutron.get_network_id(self.context, fake_name)
self.assertEqual(fake_id, network_id)
@mock.patch('magnum.common.clients.OpenStackClients')
def test_get_network_id_notfound(self, mock_clients):
fake_name = "fake_network"
fake_id = "24fe5da0-1ac0-11e9-84cd-00224d6b7bc1"
mock_nclient = mock.MagicMock()
mock_nclient.list_networks.return_value = {
'networks': [
{
'id': fake_id,
'name': fake_name,
'router:external': True
}
]
}
osc = mock.MagicMock()
mock_clients.return_value = osc
osc.neutron.return_value = mock_nclient
self.assertRaises(
exception.ExternalNetworkNotFound,
neutron.get_network_id,
self.context,
"another_network"
)
@mock.patch('magnum.common.clients.OpenStackClients')
def test_get_network_id_conflict(self, mock_clients):
fake_name = "fake_network"
fake_id_1 = "24fe5da0-1ac0-11e9-84cd-00224d6b7bc1"
fake_id_2 = "93781f82-1ac0-11e9-84cd-00224d6b7bc1"
mock_nclient = mock.MagicMock()
mock_nclient.list_networks.return_value = {
'networks': [
{
'id': fake_id_1,
'name': fake_name,
'router:external': True
},
{
'id': fake_id_2,
'name': fake_name,
'router:external': True
}
]
}
osc = mock.MagicMock()
mock_clients.return_value = osc
osc.neutron.return_value = mock_nclient
self.assertRaises(
exception.Conflict,
neutron.get_network_id,
self.context,
fake_name
)

View File

@ -33,7 +33,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'master_flavor_id': 'master_flavor_id', 'master_flavor_id': 'master_flavor_id',
'keypair_id': 'keypair_id', 'keypair_id': 'keypair_id',
'dns_nameserver': 'dns_nameserver', 'dns_nameserver': 'dns_nameserver',
'external_network_id': 'external_network_id', 'external_network_id': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'fixed_network': 'fixed_network', 'fixed_network': 'fixed_network',
'fixed_subnet': 'fixed_subnet', 'fixed_subnet': 'fixed_subnet',
'network_driver': 'network_driver', 'network_driver': 'network_driver',
@ -232,7 +232,7 @@ class TestClusterConductorWithK8s(base.TestCase):
} }
expected = { expected = {
'ssh_key_name': 'keypair_id', 'ssh_key_name': 'keypair_id',
'external_network': 'external_network_id', 'external_network': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'fixed_network': 'fixed_network', 'fixed_network': 'fixed_network',
'fixed_subnet': 'fixed_subnet', 'fixed_subnet': 'fixed_subnet',
'network_driver': 'network_driver', 'network_driver': 'network_driver',
@ -282,6 +282,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cert_manager_api': 'False', 'cert_manager_api': 'False',
'ingress_controller': 'i-controller', 'ingress_controller': 'i-controller',
'ingress_controller_role': 'i-controller-role', 'ingress_controller_role': 'i-controller-role',
'octavia_ingress_controller_tag': None,
'kubelet_options': '--kubelet', 'kubelet_options': '--kubelet',
'kubeapi_options': '--kubeapi', 'kubeapi_options': '--kubeapi',
'kubecontroller_options': '--kubecontroller', 'kubecontroller_options': '--kubecontroller',
@ -358,7 +359,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'docker_volume_size': 20, 'docker_volume_size': 20,
'docker_volume_type': 'lvmdriver-1', 'docker_volume_type': 'lvmdriver-1',
'etcd_volume_size': None, 'etcd_volume_size': None,
'external_network': 'external_network_id', 'external_network': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'fixed_network': 'fixed_network', 'fixed_network': 'fixed_network',
'fixed_subnet': 'fixed_subnet', 'fixed_subnet': 'fixed_subnet',
'flannel_backend': 'vxlan', 'flannel_backend': 'vxlan',
@ -403,6 +404,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cert_manager_api': 'False', 'cert_manager_api': 'False',
'ingress_controller': 'i-controller', 'ingress_controller': 'i-controller',
'ingress_controller_role': 'i-controller-role', 'ingress_controller_role': 'i-controller-role',
'octavia_ingress_controller_tag': None,
'kubelet_options': '--kubelet', 'kubelet_options': '--kubelet',
'kubeapi_options': '--kubeapi', 'kubeapi_options': '--kubeapi',
'kubecontroller_options': '--kubecontroller', 'kubecontroller_options': '--kubecontroller',
@ -476,7 +478,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'docker_volume_size': 20, 'docker_volume_size': 20,
'master_flavor': 'master_flavor_id', 'master_flavor': 'master_flavor_id',
'minion_flavor': 'flavor_id', 'minion_flavor': 'flavor_id',
'external_network': 'external_network_id', 'external_network': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'flannel_backend': 'vxlan', 'flannel_backend': 'vxlan',
'flannel_network_cidr': '10.101.0.0/16', 'flannel_network_cidr': '10.101.0.0/16',
'flannel_network_subnetlen': '26', 'flannel_network_subnetlen': '26',
@ -511,6 +513,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cert_manager_api': 'False', 'cert_manager_api': 'False',
'ingress_controller': 'i-controller', 'ingress_controller': 'i-controller',
'ingress_controller_role': 'i-controller-role', 'ingress_controller_role': 'i-controller-role',
'octavia_ingress_controller_tag': None,
'kubelet_options': '--kubelet', 'kubelet_options': '--kubelet',
'kubeapi_options': '--kubeapi', 'kubeapi_options': '--kubeapi',
'kubecontroller_options': '--kubecontroller', 'kubecontroller_options': '--kubecontroller',
@ -561,7 +564,7 @@ class TestClusterConductorWithK8s(base.TestCase):
expected = { expected = {
'ssh_key_name': 'keypair_id', 'ssh_key_name': 'keypair_id',
'external_network': 'external_network_id', 'external_network': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'fixed_network': 'fixed_network', 'fixed_network': 'fixed_network',
'fixed_subnet': 'fixed_subnet', 'fixed_subnet': 'fixed_subnet',
'availability_zone': 'az_1', 'availability_zone': 'az_1',
@ -612,6 +615,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cert_manager_api': 'False', 'cert_manager_api': 'False',
'ingress_controller': 'i-controller', 'ingress_controller': 'i-controller',
'ingress_controller_role': 'i-controller-role', 'ingress_controller_role': 'i-controller-role',
'octavia_ingress_controller_tag': None,
'kubelet_options': '--kubelet', 'kubelet_options': '--kubelet',
'kubeapi_options': '--kubeapi', 'kubeapi_options': '--kubeapi',
'kubecontroller_options': '--kubecontroller', 'kubecontroller_options': '--kubecontroller',
@ -657,7 +661,7 @@ class TestClusterConductorWithK8s(base.TestCase):
expected = { expected = {
'ssh_key_name': 'keypair_id', 'ssh_key_name': 'keypair_id',
'availability_zone': 'az_1', 'availability_zone': 'az_1',
'external_network': 'external_network_id', 'external_network': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'fixed_network': 'fixed_network', 'fixed_network': 'fixed_network',
'fixed_subnet': 'fixed_subnet', 'fixed_subnet': 'fixed_subnet',
'dns_nameserver': 'dns_nameserver', 'dns_nameserver': 'dns_nameserver',
@ -707,6 +711,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cert_manager_api': 'False', 'cert_manager_api': 'False',
'ingress_controller': 'i-controller', 'ingress_controller': 'i-controller',
'ingress_controller_role': 'i-controller-role', 'ingress_controller_role': 'i-controller-role',
'octavia_ingress_controller_tag': None,
'kubelet_options': '--kubelet', 'kubelet_options': '--kubelet',
'kubeapi_options': '--kubeapi', 'kubeapi_options': '--kubeapi',
'kubecontroller_options': '--kubecontroller', 'kubecontroller_options': '--kubecontroller',
@ -896,7 +901,7 @@ class TestClusterConductorWithK8s(base.TestCase):
expected = { expected = {
'ssh_key_name': 'keypair_id', 'ssh_key_name': 'keypair_id',
'external_network': 'external_network_id', 'external_network': 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e',
'fixed_network': 'fixed_network', 'fixed_network': 'fixed_network',
'fixed_subnet': 'fixed_subnet', 'fixed_subnet': 'fixed_subnet',
'dns_nameserver': 'dns_nameserver', 'dns_nameserver': 'dns_nameserver',
@ -946,6 +951,7 @@ class TestClusterConductorWithK8s(base.TestCase):
'cert_manager_api': 'False', 'cert_manager_api': 'False',
'ingress_controller': 'i-controller', 'ingress_controller': 'i-controller',
'ingress_controller_role': 'i-controller-role', 'ingress_controller_role': 'i-controller-role',
'octavia_ingress_controller_tag': None,
'kubelet_options': '--kubelet', 'kubelet_options': '--kubelet',
'kubeapi_options': '--kubeapi', 'kubeapi_options': '--kubeapi',
'kubecontroller_options': '--kubecontroller', 'kubecontroller_options': '--kubecontroller',

View File

@ -326,6 +326,8 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
mock_cluster_template.tls_disabled = False mock_cluster_template.tls_disabled = False
mock_cluster_template.registry_enabled = False mock_cluster_template.registry_enabled = False
mock_cluster_template.network_driver = 'flannel' mock_cluster_template.network_driver = 'flannel'
external_network_id = '17e4e301-b7f3-4996-b3dd-97b3a700174b'
mock_cluster_template.external_network_id = external_network_id
mock_cluster = mock.MagicMock() mock_cluster = mock.MagicMock()
mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52' mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
del mock_cluster.stack_id del mock_cluster.stack_id
@ -384,9 +386,11 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
cgroup_driver = mock_cluster.labels.get( cgroup_driver = mock_cluster.labels.get(
'cgroup_driver') 'cgroup_driver')
ingress_controller = mock_cluster.labels.get( ingress_controller = mock_cluster.labels.get(
'ingress_controller') 'ingress_controller').lower()
ingress_controller_role = mock_cluster.labels.get( ingress_controller_role = mock_cluster.labels.get(
'ingress_controller_role') 'ingress_controller_role')
octavia_ingress_controller_tag = mock_cluster.labels.get(
'octavia_ingress_controller_tag')
kubelet_options = mock_cluster.labels.get( kubelet_options = mock_cluster.labels.get(
'kubelet_options') 'kubelet_options')
kubeapi_options = mock_cluster.labels.get( kubeapi_options = mock_cluster.labels.get(
@ -459,6 +463,7 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'pods_network_cidr': pods_network_cidr, 'pods_network_cidr': pods_network_cidr,
'ingress_controller': ingress_controller, 'ingress_controller': ingress_controller,
'ingress_controller_role': ingress_controller_role, 'ingress_controller_role': ingress_controller_role,
'octavia_ingress_controller_tag': octavia_ingress_controller_tag,
'octavia_enabled': False, 'octavia_enabled': False,
'kube_service_account_key': 'public_key', 'kube_service_account_key': 'public_key',
'kube_service_account_private_key': 'private_key', 'kube_service_account_private_key': 'private_key',
@ -469,6 +474,7 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'keystone_auth_enabled': keystone_auth_enabled, 'keystone_auth_enabled': keystone_auth_enabled,
'k8s_keystone_auth_tag': k8s_keystone_auth_tag, 'k8s_keystone_auth_tag': k8s_keystone_auth_tag,
'project_id': project_id, 'project_id': project_id,
'external_network': external_network_id
}} }}
mock_get_params.assert_called_once_with(mock_context, mock_get_params.assert_called_once_with(mock_context,
mock_cluster_template, mock_cluster_template,
@ -486,6 +492,170 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
mock_cluster, mock_cluster,
) )
@mock.patch('magnum.common.neutron.get_network_id')
@mock.patch('magnum.common.keystone.is_octavia_enabled')
@mock.patch('magnum.common.clients.OpenStackClients')
@mock.patch('magnum.drivers.k8s_fedora_atomic_v1.template_def'
'.AtomicK8sTemplateDefinition.get_discovery_url')
@mock.patch('magnum.drivers.heat.template_def.BaseTemplateDefinition'
'.get_params')
@mock.patch('magnum.drivers.heat.template_def.TemplateDefinition'
'.get_output')
@mock.patch('magnum.common.x509.operations.generate_csr_and_key')
def test_k8s_get_params_external_network_id(self,
mock_generate_csr_and_key,
mock_get_output,
mock_get_params,
mock_get_discovery_url,
mock_osc_class,
mock_enable_octavia,
mock_network_id):
mock_generate_csr_and_key.return_value = {'csr': 'csr',
'private_key': 'private_key',
'public_key': 'public_key'}
mock_enable_octavia.return_value = False
mock_get_discovery_url.return_value = 'fake_discovery_url'
external_network_id = 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e'
mock_network_id.return_value = external_network_id
mock_context = mock.MagicMock()
mock_context.auth_token = 'AUTH_TOKEN'
mock_context.auth_url = 'http://192.168.10.10:5000/v3'
mock_context.user_name = 'fake_user'
mock_cluster_template = mock.MagicMock()
mock_cluster_template.tls_disabled = False
mock_cluster_template.registry_enabled = False
mock_cluster_template.network_driver = 'calico'
mock_cluster_template.external_network_id = "public"
mock_cluster = mock.MagicMock()
mock_cluster.labels = {}
mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
mock_osc = mock.MagicMock()
mock_osc.magnum_url.return_value = 'http://127.0.0.1:9511/v1'
mock_osc.cinder_region_name.return_value = 'RegionOne'
mock_osc_class.return_value = mock_osc
k8s_def = k8sa_tdef.AtomicK8sTemplateDefinition()
k8s_def.get_params(mock_context, mock_cluster_template, mock_cluster)
actual_params = mock_get_params.call_args[1]["extra_params"]
self.assertEqual(
external_network_id,
actual_params.get("external_network")
)
mock_network_id.assert_called_once_with(
mock_context,
mock_cluster_template.external_network_id
)
@mock.patch('magnum.common.keystone.is_octavia_enabled')
@mock.patch('magnum.common.clients.OpenStackClients')
@mock.patch('magnum.drivers.k8s_fedora_atomic_v1.template_def'
'.AtomicK8sTemplateDefinition.get_discovery_url')
@mock.patch('magnum.drivers.heat.template_def.BaseTemplateDefinition'
'.get_params')
@mock.patch('magnum.drivers.heat.template_def.TemplateDefinition'
'.get_output')
@mock.patch('magnum.common.x509.operations.generate_csr_and_key')
def test_k8s_get_params_octavia_disabled(self,
mock_generate_csr_and_key,
mock_get_output,
mock_get_params,
mock_get_discovery_url,
mock_osc_class,
mock_enable_octavia):
mock_generate_csr_and_key.return_value = {'csr': 'csr',
'private_key': 'private_key',
'public_key': 'public_key'}
mock_enable_octavia.return_value = False
mock_get_discovery_url.return_value = 'fake_discovery_url'
mock_context = mock.MagicMock()
mock_context.auth_token = 'AUTH_TOKEN'
mock_context.auth_url = 'http://192.168.10.10:5000/v3'
mock_context.user_name = 'fake_user'
mock_cluster_template = mock.MagicMock()
mock_cluster_template.tls_disabled = False
mock_cluster_template.registry_enabled = False
mock_cluster_template.network_driver = 'calico'
external_network_id = 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e'
mock_cluster_template.external_network_id = external_network_id
mock_cluster = mock.MagicMock()
mock_cluster.labels = {"ingress_controller": "octavia"}
mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
mock_osc = mock.MagicMock()
mock_osc.magnum_url.return_value = 'http://127.0.0.1:9511/v1'
mock_osc.cinder_region_name.return_value = 'RegionOne'
mock_osc_class.return_value = mock_osc
k8s_def = k8sa_tdef.AtomicK8sTemplateDefinition()
self.assertRaises(
exception.InvalidParameterValue,
k8s_def.get_params,
mock_context,
mock_cluster_template,
mock_cluster,
)
@mock.patch('magnum.common.keystone.is_octavia_enabled')
@mock.patch('magnum.common.clients.OpenStackClients')
@mock.patch('magnum.drivers.k8s_fedora_atomic_v1.template_def'
'.AtomicK8sTemplateDefinition.get_discovery_url')
@mock.patch('magnum.drivers.heat.template_def.BaseTemplateDefinition'
'.get_params')
@mock.patch('magnum.drivers.heat.template_def.TemplateDefinition'
'.get_output')
@mock.patch('magnum.common.x509.operations.generate_csr_and_key')
def test_k8s_get_params_octavia_enabled(self,
mock_generate_csr_and_key,
mock_get_output,
mock_get_params,
mock_get_discovery_url,
mock_osc_class,
mock_enable_octavia):
mock_generate_csr_and_key.return_value = {'csr': 'csr',
'private_key': 'private_key',
'public_key': 'public_key'}
mock_enable_octavia.return_value = True
mock_get_discovery_url.return_value = 'fake_discovery_url'
mock_context = mock.MagicMock()
mock_context.auth_token = 'AUTH_TOKEN'
mock_context.auth_url = 'http://192.168.10.10:5000/v3'
mock_context.user_name = 'fake_user'
mock_cluster_template = mock.MagicMock()
mock_cluster_template.tls_disabled = False
mock_cluster_template.registry_enabled = False
mock_cluster_template.network_driver = 'calico'
external_network_id = 'e2a6c8b0-a3c2-42a3-b3f4-01400a30896e'
mock_cluster_template.external_network_id = external_network_id
mock_cluster = mock.MagicMock()
mock_cluster.labels = {"ingress_controller": "octavia"}
mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
mock_osc = mock.MagicMock()
mock_osc.magnum_url.return_value = 'http://127.0.0.1:9511/v1'
mock_osc.cinder_region_name.return_value = 'RegionOne'
mock_osc_class.return_value = mock_osc
k8s_def = k8sa_tdef.AtomicK8sTemplateDefinition()
k8s_def.get_params(mock_context, mock_cluster_template, mock_cluster)
actual_params = mock_get_params.call_args[1]["extra_params"]
self.assertEqual(
"octavia",
actual_params.get("ingress_controller")
)
@mock.patch('magnum.common.keystone.is_octavia_enabled') @mock.patch('magnum.common.keystone.is_octavia_enabled')
@mock.patch('magnum.common.clients.OpenStackClients') @mock.patch('magnum.common.clients.OpenStackClients')
@mock.patch('magnum.drivers.heat.template_def' @mock.patch('magnum.drivers.heat.template_def'
@ -513,6 +683,8 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
mock_cluster_template.tls_disabled = True mock_cluster_template.tls_disabled = True
mock_cluster_template.registry_enabled = False mock_cluster_template.registry_enabled = False
mock_cluster_template.network_driver = 'calico' mock_cluster_template.network_driver = 'calico'
external_network_id = '17e4e301-b7f3-4996-b3dd-97b3a700174b'
mock_cluster_template.external_network_id = external_network_id
mock_cluster = mock.MagicMock() mock_cluster = mock.MagicMock()
mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52' mock_cluster.uuid = '5d12f6fd-a196-4bf0-ae4c-1f639a523a52'
del mock_cluster.stack_id del mock_cluster.stack_id
@ -571,9 +743,11 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
cgroup_driver = mock_cluster.labels.get( cgroup_driver = mock_cluster.labels.get(
'cgroup_driver') 'cgroup_driver')
ingress_controller = mock_cluster.labels.get( ingress_controller = mock_cluster.labels.get(
'ingress_controller') 'ingress_controller').lower()
ingress_controller_role = mock_cluster.labels.get( ingress_controller_role = mock_cluster.labels.get(
'ingress_controller_role') 'ingress_controller_role')
octavia_ingress_controller_tag = mock_cluster.labels.get(
'octavia_ingress_controller_tag')
kubelet_options = mock_cluster.labels.get( kubelet_options = mock_cluster.labels.get(
'kubelet_options') 'kubelet_options')
kubeapi_options = mock_cluster.labels.get( kubeapi_options = mock_cluster.labels.get(
@ -648,6 +822,7 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'pods_network_cidr': pods_network_cidr, 'pods_network_cidr': pods_network_cidr,
'ingress_controller': ingress_controller, 'ingress_controller': ingress_controller,
'ingress_controller_role': ingress_controller_role, 'ingress_controller_role': ingress_controller_role,
'octavia_ingress_controller_tag': octavia_ingress_controller_tag,
'octavia_enabled': False, 'octavia_enabled': False,
'kube_service_account_key': 'public_key', 'kube_service_account_key': 'public_key',
'kube_service_account_private_key': 'private_key', 'kube_service_account_private_key': 'private_key',
@ -658,6 +833,7 @@ class AtomicK8sTemplateDefinitionTestCase(BaseK8sTemplateDefinitionTestCase):
'keystone_auth_enabled': keystone_auth_enabled, 'keystone_auth_enabled': keystone_auth_enabled,
'k8s_keystone_auth_tag': k8s_keystone_auth_tag, 'k8s_keystone_auth_tag': k8s_keystone_auth_tag,
'project_id': project_id, 'project_id': project_id,
'external_network': external_network_id
}} }}
mock_get_params.assert_called_once_with(mock_context, mock_get_params.assert_called_once_with(mock_context,
mock_cluster_template, mock_cluster_template,

View File

@ -0,0 +1,8 @@
---
features:
- |
Add a new option 'octavia' for the label 'ingress_controller' and a new
label 'octavia_ingress_controller_tag' to enable the deployment of
`octavia-ingress-controller <https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/using-octavia-ingress-controller.md>`_
in the kubernetes cluster. The 'ingress_controller_role' label is not used
for this option.