Merge "Adding node-labels api"
This commit is contained in:
commit
290448fe83
1
.gitignore
vendored
1
.gitignore
vendored
@ -10,6 +10,7 @@
|
||||
/.cache
|
||||
/.eggs
|
||||
/.helm-pid
|
||||
/.pytest_cache
|
||||
/.tox
|
||||
/build
|
||||
/conformance
|
||||
|
@ -62,3 +62,33 @@ Responses:
|
||||
|
||||
+ 200 OK: Documents were successfully validated
|
||||
+ 400 Bad Request: Documents were not successfully validated
|
||||
|
||||
|
||||
/v1.0/node-labels/<node_name>
|
||||
-----------------------------
|
||||
|
||||
Update node labels
|
||||
|
||||
PUT /v1.0/node-labels/<node_name>
|
||||
|
||||
Updates node labels eg: adding new labels, overriding existing
|
||||
labels and deleting labels from a node.
|
||||
|
||||
Message Body:
|
||||
|
||||
dict of labels
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{"label-a": "value1", "label-b": "value2", "label-c": "value3"}
|
||||
|
||||
Responses:
|
||||
|
||||
+ 200 OK: Labels successfully updated
|
||||
+ 400 Bad Request: Bad input format
|
||||
+ 401 Unauthorized: Unauthenticated access
|
||||
+ 403 Forbidden: Unauthorized access
|
||||
+ 404 Not Found: Bad URL or Node not found
|
||||
+ 500 Internal Server Error: Server error encountered
|
||||
+ 502 Bad Gateway: Kubernetes Config Error
|
||||
+ 503 Service Unavailable: Failed to interact with Kubernetes API
|
||||
|
@ -39,3 +39,18 @@ Promenade Exceptions
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:undoc-members:
|
||||
* - KubernetesConfigException
|
||||
- .. autoexception:: promenade.exceptions.KubernetesConfigException
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:undoc-members:
|
||||
* - KubernetesApiError
|
||||
- .. autoexception:: promenade.exceptions.KubernetesApiError
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:undoc-members:
|
||||
* - NodeNotFoundException
|
||||
- .. autoexception:: promenade.exceptions.NodeNotFoundException
|
||||
:members:
|
||||
:show-inheritance:
|
||||
:undoc-members:
|
||||
|
@ -19,6 +19,7 @@ from promenade.control.health_api import HealthResource
|
||||
from promenade.control.join_scripts import JoinScriptsResource
|
||||
from promenade.control.middleware import (AuthMiddleware, ContextMiddleware,
|
||||
LoggingMiddleware)
|
||||
from promenade.control.node_labels import NodeLabelsResource
|
||||
from promenade.control.validatedesign import ValidateDesignResource
|
||||
from promenade import exceptions as exc
|
||||
from promenade import logging
|
||||
@ -41,6 +42,7 @@ def start_api():
|
||||
('/health', HealthResource()),
|
||||
('/join-scripts', JoinScriptsResource()),
|
||||
('/validatedesign', ValidateDesignResource()),
|
||||
('/node-labels/{node_name}', NodeLabelsResource()),
|
||||
]
|
||||
|
||||
# Set up the 1.0 routes
|
||||
|
44
promenade/control/node_labels.py
Normal file
44
promenade/control/node_labels.py
Normal file
@ -0,0 +1,44 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import falcon
|
||||
|
||||
from promenade.control.base import BaseResource
|
||||
from promenade.kubeclient import KubeClient
|
||||
from promenade import exceptions
|
||||
from promenade import logging
|
||||
from promenade import policy
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NodeLabelsResource(BaseResource):
|
||||
"""Class for Node Labels Manage API"""
|
||||
|
||||
@policy.ApiEnforcer('kubernetes_provisioner:update_node_labels')
|
||||
def on_put(self, req, resp, node_name=None):
|
||||
json_data = self.req_json(req)
|
||||
if node_name is None:
|
||||
LOG.error("Invalid format error: Missing input: node_name")
|
||||
raise exceptions.InvalidFormatError(
|
||||
description="Missing input: node_name")
|
||||
if json_data is None:
|
||||
LOG.error("Invalid format error: Missing input: labels dict")
|
||||
raise exceptions.InvalidFormatError(
|
||||
description="Missing input: labels dict")
|
||||
kubeclient = KubeClient()
|
||||
response = kubeclient.update_node_labels(node_name, json_data)
|
||||
|
||||
resp.body = response
|
||||
resp.status = falcon.HTTP_200
|
@ -295,6 +295,14 @@ class InvalidFormatError(PromenadeException):
|
||||
title = 'Invalid Input Error'
|
||||
status = falcon.HTTP_400
|
||||
|
||||
def __init__(self, title="", description=""):
|
||||
if not title:
|
||||
title = self.title
|
||||
if not description:
|
||||
description = self.title
|
||||
super(InvalidFormatError, self).__init__(
|
||||
title, description, status=self.status)
|
||||
|
||||
|
||||
class ValidationException(PromenadeException):
|
||||
"""
|
||||
@ -320,6 +328,21 @@ class TemplateRenderException(PromenadeException):
|
||||
status = falcon.HTTP_500
|
||||
|
||||
|
||||
class KubernetesConfigException(PromenadeException):
|
||||
title = 'Kubernetes Config Error'
|
||||
status = falcon.HTTP_502
|
||||
|
||||
|
||||
class KubernetesApiError(PromenadeException):
|
||||
title = 'Kubernetes API Error'
|
||||
status = falcon.HTTP_503
|
||||
|
||||
|
||||
class NodeNotFoundException(KubernetesApiError):
|
||||
title = 'Node not found'
|
||||
status = falcon.HTTP_404
|
||||
|
||||
|
||||
def massage_error_list(error_list, placeholder_description):
|
||||
"""
|
||||
Returns a best-effort attempt to make a nice error list
|
||||
|
136
promenade/kubeclient.py
Normal file
136
promenade/kubeclient.py
Normal file
@ -0,0 +1,136 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import falcon
|
||||
import kubernetes
|
||||
from kubernetes.client.rest import ApiException
|
||||
from urllib3.exceptions import MaxRetryError
|
||||
|
||||
from promenade import logging
|
||||
from promenade.exceptions import KubernetesApiError
|
||||
from promenade.exceptions import KubernetesConfigException
|
||||
from promenade.exceptions import NodeNotFoundException
|
||||
from promenade.utils.success_message import SuccessMessage
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class KubeClient(object):
|
||||
"""
|
||||
Class for Kubernetes APIs client
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
""" Set Kubernetes APIs connection """
|
||||
try:
|
||||
LOG.info('Loading in-cluster Kubernetes configuration.')
|
||||
kubernetes.config.load_incluster_config()
|
||||
except kubernetes.config.config_exception.ConfigException:
|
||||
LOG.debug('Failed to load in-cluster configuration')
|
||||
try:
|
||||
LOG.info('Loading out-of-cluster Kubernetes configuration.')
|
||||
kubernetes.config.load_kube_config()
|
||||
except FileNotFoundError:
|
||||
LOG.exception(
|
||||
'FileNotFoundError: Failed to load Kubernetes config file.'
|
||||
)
|
||||
raise KubernetesConfigException
|
||||
self.client = kubernetes.client.CoreV1Api()
|
||||
|
||||
def update_node_labels(self, node_name, input_labels):
|
||||
"""
|
||||
Updating node labels
|
||||
|
||||
Args:
|
||||
node_name(str): node for which updating labels
|
||||
input_labels(dict): input labels dict
|
||||
Returns:
|
||||
SuccessMessage(dict): API success response
|
||||
"""
|
||||
resp_body_succ = SuccessMessage('Update node labels', falcon.HTTP_200)
|
||||
|
||||
try:
|
||||
existing_labels = self.get_node_labels(node_name)
|
||||
update_labels = _get_update_labels(existing_labels, input_labels)
|
||||
# If there is a change
|
||||
if bool(update_labels):
|
||||
body = {"metadata": {"labels": update_labels}}
|
||||
self.client.patch_node(node_name, body)
|
||||
return resp_body_succ.get_output_json()
|
||||
except (ApiException, MaxRetryError) as e:
|
||||
LOG.exception(
|
||||
"An exception occurred during node labels update: " + str(e))
|
||||
raise KubernetesApiError
|
||||
|
||||
def get_node_labels(self, node_name):
|
||||
"""
|
||||
Get existing registered node labels
|
||||
|
||||
Args:
|
||||
node_name(str): node of which getting labels
|
||||
Returns:
|
||||
dict: labels dict
|
||||
"""
|
||||
try:
|
||||
response = self.client.read_node(node_name)
|
||||
if response is not None:
|
||||
return response.metadata.labels
|
||||
else:
|
||||
return {}
|
||||
except (ApiException, MaxRetryError) as e:
|
||||
LOG.exception(
|
||||
"An exception occurred in fetching node labels: " + str(e))
|
||||
if hasattr(e, 'status') and str(e.status) == "404":
|
||||
raise NodeNotFoundException
|
||||
else:
|
||||
raise KubernetesApiError
|
||||
|
||||
|
||||
def _get_update_labels(existing_labels, input_labels):
|
||||
"""
|
||||
Helper function to add new labels, delete labels, override
|
||||
existing labels
|
||||
|
||||
Args:
|
||||
existing_labels(dict): Existing node labels
|
||||
input_labels(dict): Input/Req. labels
|
||||
Returns:
|
||||
update_labels(dict): Node labels to be updated
|
||||
or
|
||||
input_labels(dict): Node labels to be updated
|
||||
"""
|
||||
update_labels = {}
|
||||
|
||||
# no existing labels found
|
||||
if not existing_labels:
|
||||
# filter delete label request since there is no labels set on a node
|
||||
update_labels.update(
|
||||
{k: v
|
||||
for k, v in input_labels.items() if v is not None})
|
||||
return update_labels
|
||||
|
||||
# new labels or overriding labels
|
||||
update_labels.update({
|
||||
k: v
|
||||
for k, v in input_labels.items()
|
||||
if k not in existing_labels or v != existing_labels[k]
|
||||
})
|
||||
|
||||
# deleted labels
|
||||
update_labels.update({
|
||||
k: None
|
||||
for k in existing_labels.keys()
|
||||
if k not in input_labels and "kubernetes.io" not in k
|
||||
})
|
||||
return update_labels
|
@ -41,6 +41,12 @@ POLICIES = [
|
||||
'path': '/api/v1.0/validatedesign',
|
||||
'method': 'POST'
|
||||
}]),
|
||||
op.DocumentedRuleDefault('kubernetes_provisioner:update_node_labels',
|
||||
'role:admin', 'Update Node Labels',
|
||||
[{
|
||||
'path': '/api/v1.0/node-labels/{node_name}',
|
||||
'method': 'PUT'
|
||||
}]),
|
||||
]
|
||||
|
||||
|
||||
|
37
promenade/utils/message.py
Normal file
37
promenade/utils/message.py
Normal file
@ -0,0 +1,37 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import json
|
||||
|
||||
|
||||
class Message(object):
|
||||
"""Message base class"""
|
||||
|
||||
def __init__(self):
|
||||
self.error_count = 0
|
||||
self.details = {'errorCount': 0, 'messageList': []}
|
||||
self.output = {
|
||||
'kind': 'Status',
|
||||
'apiVersion': 'v1.0',
|
||||
'metadata': {},
|
||||
'details': self.details
|
||||
}
|
||||
|
||||
def get_output_json(self):
|
||||
"""Returns message as JSON.
|
||||
|
||||
:returns: Message formatted in JSON.
|
||||
:rtype: json
|
||||
"""
|
||||
return json.dumps(self.output, indent=2)
|
32
promenade/utils/success_message.py
Normal file
32
promenade/utils/success_message.py
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import falcon
|
||||
|
||||
from promenade.utils.message import Message
|
||||
|
||||
|
||||
class SuccessMessage(Message):
|
||||
"""SuccessMessage per UCP convention:
|
||||
https://airshipit.readthedocs.io/en/latest/api-conventions.html#status-responses
|
||||
"""
|
||||
|
||||
def __init__(self, reason='', code=falcon.HTTP_200):
|
||||
super(SuccessMessage, self).__init__()
|
||||
self.output.update({
|
||||
'status': 'Success',
|
||||
'message': '',
|
||||
'reason': reason,
|
||||
'code': code
|
||||
})
|
@ -15,8 +15,10 @@
|
||||
import falcon
|
||||
import json
|
||||
|
||||
from promenade.utils.message import Message
|
||||
|
||||
class ValidationMessage(object):
|
||||
|
||||
class ValidationMessage(Message):
|
||||
""" ValidationMessage per UCP convention:
|
||||
https://github.com/att-comdev/ucp-integration/blob/master/docs/source/api-conventions.rst#output-structure # noqa
|
||||
|
||||
@ -34,15 +36,8 @@ class ValidationMessage(object):
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.error_count = 0
|
||||
self.details = {'errorCount': 0, 'messageList': []}
|
||||
self.output = {
|
||||
'kind': 'Status',
|
||||
'apiVersion': 'v1.0',
|
||||
'metadata': {},
|
||||
'reason': 'Validation',
|
||||
'details': self.details,
|
||||
}
|
||||
super(ValidationMessage, self).__init__()
|
||||
self.output.update({'reason': 'Validation'})
|
||||
|
||||
def add_error_message(self,
|
||||
msg,
|
||||
@ -80,14 +75,6 @@ class ValidationMessage(object):
|
||||
self.output['status'] = 'Success'
|
||||
return self.output
|
||||
|
||||
def get_output_json(self):
|
||||
""" Return ValidationMessage message as JSON.
|
||||
|
||||
:returns: The ValidationMessage formatted in JSON, for logging.
|
||||
:rtype: json
|
||||
"""
|
||||
return json.dumps(self.output, indent=2)
|
||||
|
||||
def update_response(self, resp):
|
||||
output = self.get_output()
|
||||
resp.status = output['code']
|
||||
|
101
tests/unit/api/test_kubeclient.py
Normal file
101
tests/unit/api/test_kubeclient.py
Normal file
@ -0,0 +1,101 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import pytest
|
||||
|
||||
from promenade import kubeclient
|
||||
|
||||
TEST_DATA = [(
|
||||
'Multi-facet update',
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"label-c": "value3",
|
||||
},
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-c": "value4",
|
||||
"label-d": "value99",
|
||||
},
|
||||
{
|
||||
"label-b": None,
|
||||
"label-c": "value4",
|
||||
"label-d": "value99",
|
||||
},
|
||||
), (
|
||||
'Add labels when none exist',
|
||||
None,
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"label-c": "value3",
|
||||
},
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"label-c": "value3",
|
||||
},
|
||||
), (
|
||||
'No updates',
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"label-c": "value3",
|
||||
},
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"label-c": "value3",
|
||||
},
|
||||
{},
|
||||
), (
|
||||
'Delete labels',
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"label-c": "value3",
|
||||
},
|
||||
{},
|
||||
{
|
||||
"label-a": None,
|
||||
"label-b": None,
|
||||
"label-c": None,
|
||||
},
|
||||
), (
|
||||
'Delete labels when none',
|
||||
None,
|
||||
{},
|
||||
{},
|
||||
), (
|
||||
'Avoid kubernetes.io labels Deletion',
|
||||
{
|
||||
"label-a": "value1",
|
||||
"label-b": "value2",
|
||||
"kubernetes.io/hostname": "ubutubox",
|
||||
},
|
||||
{
|
||||
"label-a": "value99",
|
||||
},
|
||||
{
|
||||
"label-a": "value99",
|
||||
"label-b": None,
|
||||
},
|
||||
)]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('description,existing_lbl,input_lbl,expected',
|
||||
TEST_DATA)
|
||||
def test_get_update_labels(description, existing_lbl, input_lbl, expected):
|
||||
applied = kubeclient._get_update_labels(existing_lbl, input_lbl)
|
||||
assert applied == expected
|
88
tests/unit/api/test_update_labels.py
Normal file
88
tests/unit/api/test_update_labels.py
Normal file
@ -0,0 +1,88 @@
|
||||
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import falcon
|
||||
import json
|
||||
import pytest
|
||||
|
||||
from falcon import testing
|
||||
from promenade import promenade
|
||||
from promenade.utils.success_message import SuccessMessage
|
||||
from unittest import mock
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client():
|
||||
return testing.TestClient(promenade.start_promenade(disable='keystone'))
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def req_header():
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'X-IDENTITY-STATUS': 'Confirmed',
|
||||
'X-USER-NAME': 'Test',
|
||||
'X-ROLES': 'admin'
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def req_body():
|
||||
return json.dumps({
|
||||
"label-a": "value1",
|
||||
"label-c": "value4",
|
||||
"label-d": "value99"
|
||||
})
|
||||
|
||||
|
||||
@mock.patch('promenade.kubeclient.KubeClient.update_node_labels')
|
||||
@mock.patch('promenade.kubeclient.KubeClient.__init__')
|
||||
def test_node_labels_pass(mock_kubeclient, mock_update_node_labels, client,
|
||||
req_header, req_body):
|
||||
"""
|
||||
Function to test node labels pass test case
|
||||
|
||||
Args:
|
||||
mock_kubeclient: mock KubeClient object
|
||||
mock_update_node_labels: mock update_node_labels object
|
||||
client: Promenode APIs test client
|
||||
req_header: API request header
|
||||
req_body: API request body
|
||||
"""
|
||||
mock_kubeclient.return_value = None
|
||||
mock_update_node_labels.return_value = _mock_update_node_labels()
|
||||
response = client.simulate_put(
|
||||
'/api/v1.0/node-labels/ubuntubox', headers=req_header, body=req_body)
|
||||
assert response.status == falcon.HTTP_200
|
||||
assert response.json["status"] == "Success"
|
||||
|
||||
|
||||
def test_node_labels_missing_inputs(client, req_header, req_body):
|
||||
"""
|
||||
Function to test node labels missing inputs
|
||||
|
||||
Args:
|
||||
client: Promenode APIs test client
|
||||
req_header: API request header
|
||||
req_body: API request body
|
||||
"""
|
||||
response = client.simulate_post(
|
||||
'/api/v1.0/node-labels', headers=req_header, body=req_body)
|
||||
assert response.status == falcon.HTTP_404
|
||||
|
||||
|
||||
def _mock_update_node_labels():
|
||||
"""Mock update_node_labels function"""
|
||||
resp_body_succ = SuccessMessage('Update node labels')
|
||||
return resp_body_succ.get_output_json()
|
@ -61,3 +61,8 @@ promenade_health_check() {
|
||||
sleep 10
|
||||
done
|
||||
}
|
||||
|
||||
promenade_put_labels_url() {
|
||||
NODE_NAME=${1}
|
||||
echo "${PROMENADE_BASE_URL}/api/v1.0/node-labels/${NODE_NAME}"
|
||||
}
|
||||
|
@ -1,32 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
set -eu
|
||||
|
||||
source "${GATE_UTILS}"
|
||||
|
||||
log Adding labels to node n0
|
||||
kubectl_cmd n1 label node n0 \
|
||||
calico-etcd=enabled \
|
||||
kubernetes-apiserver=enabled \
|
||||
kubernetes-controller-manager=enabled \
|
||||
kubernetes-etcd=enabled \
|
||||
kubernetes-scheduler=enabled
|
||||
VIA="n1"
|
||||
|
||||
# XXX Need to wait
|
||||
CURL_ARGS=("--fail" "--max-time" "300" "--retry" "16" "--retry-delay" "15")
|
||||
|
||||
log Adding labels to node n0
|
||||
JSON="{\"calico-etcd\": \"enabled\", \"coredns\": \"enabled\", \"kubernetes-apiserver\": \"enabled\", \"kubernetes-controller-manager\": \"enabled\", \"kubernetes-etcd\": \"enabled\", \"kubernetes-scheduler\": \"enabled\", \"ucp-control-plane\": \"enabled\"}"
|
||||
|
||||
ssh_cmd "${VIA}" curl -v "${CURL_ARGS[@]}" -X PUT -H "Content-Type: application/json" -d "${JSON}" "$(promenade_put_labels_url n0)"
|
||||
|
||||
# Need to wait
|
||||
sleep 60
|
||||
|
||||
validate_etcd_membership kubernetes n1 n0 n1 n2 n3
|
||||
validate_etcd_membership calico n1 n0 n1 n2 n3
|
||||
|
||||
log Removing labels from node n2
|
||||
kubectl_cmd n1 label node n2 \
|
||||
calico-etcd- \
|
||||
kubernetes-apiserver- \
|
||||
kubernetes-controller-manager- \
|
||||
kubernetes-etcd- \
|
||||
kubernetes-scheduler-
|
||||
JSON="{\"coredns\": \"enabled\", \"ucp-control-plane\": \"enabled\"}"
|
||||
|
||||
# XXX Need to wait
|
||||
ssh_cmd "${VIA}" curl -v "${CURL_ARGS[@]}" -X PUT -H "Content-Type: application/json" -d "${JSON}" "$(promenade_put_labels_url n2)"
|
||||
|
||||
# Need to wait
|
||||
sleep 60
|
||||
|
||||
validate_cluster n1
|
||||
|
Loading…
x
Reference in New Issue
Block a user