# Copyright 2017 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. from falcon import testing from promenade import promenade from promenade.control import health_api from unittest import mock import copy import falcon import json import pytest @pytest.fixture() def client(): return testing.TestClient(promenade.start_promenade(disable='keystone')) @pytest.fixture() def std_headers(): return { 'Content-Type': 'application/json', 'X-IDENTITY-STATUS': 'Confirmed', 'X-USER-NAME': 'Test', 'X-ROLES': 'admin' } @pytest.fixture() def std_body(): return json.dumps({ 'rel': 'design', 'href': 'http://localhost:9999', 'type': 'application/x-yaml', }) def test_post_validatedesign_empty_docs(client, std_body, std_headers): with mock.patch('promenade.design_ref.get_documents') as gd: gd.return_value = ([], False) response = client.simulate_post('/api/v1.0/validatedesign', headers=std_headers, body=std_body) assert response.status == falcon.HTTP_400 assert response.json['details']['errorCount'] == 5 VALID_DOCS = [ { 'data': { 'config': { 'insecure-registries': ['registry:5000'], 'live-restore': True, 'max-concurrent-downloads': 10, 'oom-score-adjust': -999, 'storage-driver': 'overlay2' } }, 'metadata': { 'layeringDefinition': { 'abstract': False, 'layer': 'site' }, 'name': 'docker', 'schema': 'metadata/Document/v1', 'storagePolicy': 'cleartext' }, 'schema': 'promenade/Docker/v1' }, { 'data': { 'apiserver': { 'command_prefix': [ '/apiserver', '--authorization-mode=Node,RBAC', '--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds', '--service-cluster-ip-range=10.96.0.0/16', '--endpoint-reconciler-type=lease' ] }, 'armada': { 'target_manifest': 'cluster-bootstrap' }, 'files': [{ 'content': '# placeholder for triggering calico etcd bootstrapping', 'mode': 420, 'path': '/var/lib/anchor/calico-etcd-bootstrap' }], 'hostname': 'n0', 'images': { 'armada': 'quay.io/airshipit/armada:master-ubuntu_bionic', 'kubernetes': { 'apiserver': 'registry.k8s.io/kube-apiserver-amd64:v1.26.0', 'controller-manager': 'registry.k8s.io/kube-controller-manager-amd64:v1.26.0', 'etcd': 'quay.io/coreos/etcd:v3.5.4', 'scheduler': 'registry.k8s.io/kube-scheduler-amd64:v1.26.0' } }, 'ip': '192.168.77.10', 'labels': { 'dynamic': [ 'calico-etcd=enabled', 'coredns=enabled', 'kubernetes-apiserver=enabled', 'kubernetes-controller-manager=enabled', 'kubernetes-etcd=enabled', 'kubernetes-scheduler=enabled', 'promenade-genesis=enabled', 'ucp-control-plane=enabled' ] } }, 'metadata': { 'layeringDefinition': { 'abstract': False, 'layer': 'site' }, 'name': 'genesis', 'schema': 'metadata/Document/v1' }, 'schema': 'promenade/Genesis/v1' }, { 'data': { 'files': [{ 'mode': 365, 'path': '/opt/kubernetes/bin/kubelet', 'tar_path': 'kubernetes/node/bin/kubelet', 'tar_url': 'https://dl.k8s.io/v1.26.0/kubernetes-node-linux-amd64.tar.gz' }, { 'content': '/var/lib/docker/containers/*/*-json.log\n{\n compress\n copytruncate\n create 0644 root root\n daily\n dateext\n dateformat -%Y%m%d-%s\n maxsize 10M\n missingok\n notifempty\n su root root\n rotate 1\n}', 'mode': 292, 'path': '/etc/logrotate.d/json-logrotate' }], 'images': { 'haproxy': 'haproxy:1.8.3', 'helm': { 'helm': 'lachlanevenson/k8s-helm:v3.9.4' } }, 'packages': { 'additional': ['curl', 'jq'], 'keys': [ '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\n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\nYT90qFF93M3v01BbxP+EIY2/9tiIPbrd\n=0YYh\n-----END PGP PUBLIC KEY BLOCK-----' ], 'repositories': [ 'deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable' ], 'required': { 'docker': 'docker-ce=5:19.03.8~3-0~ubuntu-bionic', 'socat': 'socat=1.7.3.1-1' } }, 'validation': { 'pod_logs': { 'image': 'busybox:1.28.3' } } }, 'metadata': { 'layeringDefinition': { 'abstract': False, 'layer': 'site' }, 'name': 'host-system', 'schema': 'metadata/Document/v1' }, 'schema': 'promenade/HostSystem/v1' }, { 'data': { 'arguments': ['--v=5'], 'images': { 'pause': 'registry.k8s.io/pause-amd64:3.1' }, 'config_file_overrides': { 'evictionMaxPodGracePeriod': -1, 'nodeStatusUpdateFrequency': '5s', 'serializeImagePulls': 'false' } }, 'metadata': { 'layeringDefinition': { 'abstract': False, 'layer': 'site' }, 'name': 'kubelet', 'schema': 'metadata/Document/v1', 'storagePolicy': 'cleartext' }, 'schema': 'promenade/Kubelet/v1' }, { 'data': { 'dns': { 'bootstrap_validation_checks': [ 'calico-etcd.kube-system.svc.cluster.local', 'google.com', 'kubernetes-etcd.kube-system.svc.cluster.local', 'kubernetes.default.svc.cluster.local' ], 'cluster_domain': 'cluster.local', 'service_ip': '10.96.0.10', 'upstream_servers': ['8.8.8.8', '8.8.4.4'] }, 'etcd': { 'container_port': 2379, 'haproxy_port': 2378 }, 'hosts_entries': [{ 'ip': '192.168.77.1', 'names': ['registry'] }], 'kubernetes': { 'apiserver_port': 6443, 'haproxy_port': 6553, 'pod_cidr': '10.97.0.0/16', 'service_cidr': '10.96.0.0/16', 'service_ip': '10.96.0.1' } }, 'metadata': { 'layeringDefinition': { 'abstract': False, 'layer': 'site' }, 'name': 'kubernetes-network', 'schema': 'metadata/Document/v1', 'storagePolicy': 'cleartext' }, 'schema': 'promenade/KubernetesNetwork/v1' }, ] def test_post_validatedesign_valid_docs(client, std_body, std_headers): with mock.patch('promenade.design_ref.get_documents') as gd: gd.return_value = (VALID_DOCS, False) response = client.simulate_post('/api/v1.0/validatedesign', headers=std_headers, body=std_body) assert response.status == falcon.HTTP_200 assert response.json['details']['errorCount'] == 0