diff --git a/.basedpyright/baseline.json b/.basedpyright/baseline.json index 0d1417fc49e..821e000be6e 100644 --- a/.basedpyright/baseline.json +++ b/.basedpyright/baseline.json @@ -1,1069 +1,5 @@ { "files": { - "./monitoring/deployment_manager/actions/dss/v1/crdb.py": [ - { - "code": "reportAssignmentType", - "range": { - "startColumn": 23, - "endColumn": 5, - "lineCount": 3 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 39, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 62, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 38, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportAssignmentType", - "range": { - "startColumn": 23, - "endColumn": 5, - "lineCount": 3 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 39, - "endColumn": 43, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 62, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportOptionalSubscript", - "range": { - "startColumn": 38, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 24, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 47, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 82, - "endColumn": 86, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 35, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 71, - "endColumn": 75, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 24, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 47, - "endColumn": 49, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 82, - "endColumn": 86, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/actions/test/hello_world.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 63, - "endColumn": 65, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 66, - "endColumn": 75, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 57, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 58, - "endColumn": 73, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 24, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 55, - "endColumn": 57, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 58, - "endColumn": 67, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 44, - "endColumn": 46, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 47, - "endColumn": 56, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 100, - "endColumn": 104, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 30, - "endColumn": 45, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 81, - "endColumn": 85, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 64, - "endColumn": 68, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 107, - "endColumn": 111, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 39, - "endColumn": 54, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 29, - "endColumn": 33, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 75, - "endColumn": 79, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/comparisons.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 23, - "endColumn": 29, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/crdb_cluster_api.py": [ - { - "code": "reportReturnType", - "range": { - "startColumn": 43, - "endColumn": 61, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/deployments.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 79, - "endColumn": 83, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 21, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 51, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 25, - "endColumn": 33, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 59, - "endColumn": 63, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/ingresses.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 76, - "endColumn": 80, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 25, - "endColumn": 29, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 55, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 29, - "endColumn": 37, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 38, - "endColumn": 42, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 63, - "endColumn": 67, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/namespaces.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 78, - "endColumn": 82, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 58, - "endColumn": 62, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/port_forwarding.py": [ - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 23, - "endColumn": 31, - "lineCount": 1 - } - }, - { - "code": "reportIncompatibleMethodOverride", - "range": { - "startColumn": 8, - "endColumn": 17, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 16, - "endColumn": 28, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 17, - "endColumn": 24, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 20, - "endColumn": 33, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/secrets.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 75, - "endColumn": 79, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 66, - "endColumn": 70, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 54, - "endColumn": 58, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 28, - "endColumn": 36, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 37, - "endColumn": 41, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 62, - "endColumn": 66, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/services.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 76, - "endColumn": 80, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 21, - "endColumn": 25, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 51, - "endColumn": 55, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 25, - "endColumn": 33, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 34, - "endColumn": 38, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 59, - "endColumn": 63, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/deploylib/systems.py": [ - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 129, - "endColumn": 133, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 83, - "endColumn": 87, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 57, - "endColumn": 64, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 83, - "endColumn": 87, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 54, - "endColumn": 61, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 75, - "endColumn": 79, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 53, - "endColumn": 60, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 83, - "endColumn": 87, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportOptionalMemberAccess", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - }, - { - "code": "reportAttributeAccessIssue", - "range": { - "startColumn": 51, - "endColumn": 59, - "lineCount": 1 - } - } - ], - "./monitoring/deployment_manager/infrastructure.py": [ - { - "code": "reportCallIssue", - "range": { - "startColumn": 12, - "endColumn": 21, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 12, - "endColumn": 21, - "lineCount": 1 - } - }, - { - "code": "reportCallIssue", - "range": { - "startColumn": 43, - "endColumn": 52, - "lineCount": 1 - } - }, - { - "code": "reportArgumentType", - "range": { - "startColumn": 43, - "endColumn": 52, - "lineCount": 1 - } - } - ], "./monitoring/mock_uss/auth.py": [ { "code": "reportArgumentType", diff --git a/NEXT_RELEASE_NOTES.md b/NEXT_RELEASE_NOTES.md index 21d0564e338..c25531f0e70 100644 --- a/NEXT_RELEASE_NOTES.md +++ b/NEXT_RELEASE_NOTES.md @@ -43,3 +43,5 @@ The release notes should contain at least the following sections: ## Optional migration tasks ## Important information + +The obsolete deployment_manager tool has been removed from this repository. diff --git a/monitoring/deployment_manager/.gitignore b/monitoring/deployment_manager/.gitignore deleted file mode 100644 index debbde8822c..00000000000 --- a/monitoring/deployment_manager/.gitignore +++ /dev/null @@ -1 +0,0 @@ -sites diff --git a/monitoring/deployment_manager/README.md b/monitoring/deployment_manager/README.md deleted file mode 100644 index 4e656225395..00000000000 --- a/monitoring/deployment_manager/README.md +++ /dev/null @@ -1,46 +0,0 @@ -## Purpose - -deployment_manager is an automation tool that will be capable of performing most of the steps a human would otherwise have to manually execute to: - -* Deploy a DSS instance, both [locally](https://github.com/interuss/dss/blob/master/build/dev/standalone_instance.md) and [in production](https://github.com/interuss/dss/blob/master/build/README.md) -* [Troubleshoot a DSS instance](https://github.com/interuss/dss/blob/master/build/README.md#troubleshooting) -* [Form or maintain a DSS pool](https://github.com/interuss/dss/blob/master/build/pooling.md) -* [Migrate database schemas](https://github.com/interuss/dss/blob/master/build/README.md#upgrading-database-schemas) -* [Upgrade CockroachDB nodes](https://www.cockroachlabs.com/docs/stable/upgrade-cockroach-version.html) -* Deploy a set of diagnostic tools ([uss_qualifier](../uss_qualifier), [mock_uss](../mock_uss)) - -deployment_manager actions all accept a declarative definition of the InterUSS deployment desired. To make changes to a deployment, the administrator should edit the development definition and then execute the desired action. - -## Usage - -To use deployment_manager, first define an InterUSS deployment in a JSON file according to the [DeploymentSpec](systems/configuration.py) schema. From a context with kubectl already configured to work with the target Kubernetes cluster, run [deployment_manager.py](deployment_manager.py) according to its help (`python3 deployment_manager.py --help`). - -### Prerequisites - -To prepare a system to run deployment manager, the following steps must be taken (commands relative to the root of this repository): - -1. Install `monitorlib` requirements (`pip3 install -r monitoring/monitorlib/requirements.txt`) -1. Install `deployment_manager` requirements (`pip3 install -r monitoring/deployment_manager/requirements.txt`) -1. Make `monitoring` accessible as a Python module source (`export PYTHONPATH=$(pwd)`) -1. See the syntax for running deployment_manager (`python3 monitoring/deployment_manager/deployment_manager.py --help`) - -## Local development - -deployment_manager is designed to interact with deployments managed with Kubernetes. To host a Kubernetes deployment on your local development machine, the use of a minikube cluster is recommended. - -### Install minikube - -* Follow [installation instructions](https://minikube.sigs.k8s.io/docs/start/) up to, and including, "Start your cluster" -* [Enable ingress](https://kubernetes.io/docs/tasks/access-application-cluster/ingress-minikube/) at 127.0.0.1 with `minikube addons enable ingress` - -### minikube usage - -If minikube is already installed, then start a session with `minikube start`. - -If kubectl is not currently configured to control minikube: `kubectl config use-context minikube` - -View the minikube dashboard with `minikube dashboard`. - -### Hello world - -The actions in [test/hello_world](actions/test/hello_world.py) demonstrate manipulation of [a simple system](actions/test/README.md) using deployment_manager; see [the README](actions/test/README.md) for actions that can be performed on this system. diff --git a/monitoring/deployment_manager/actions/__init__.py b/monitoring/deployment_manager/actions/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/actions/dss/__init__.py b/monitoring/deployment_manager/actions/dss/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/actions/dss/v1/__init__.py b/monitoring/deployment_manager/actions/dss/v1/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/actions/dss/v1/common.py b/monitoring/deployment_manager/actions/dss/v1/common.py deleted file mode 100644 index a45ace70ffb..00000000000 --- a/monitoring/deployment_manager/actions/dss/v1/common.py +++ /dev/null @@ -1,15 +0,0 @@ -import functools - -from monitoring.deployment_manager.infrastructure import Context - - -def requires_v1_dss(func): - """Make sure v1 DSS is well-specified""" - - @functools.wraps(func) - def wrapper_v1_dss_required(context: Context, *args, **kwargs): - if "dss" not in context.spec: - raise ValueError("DSS system is not defined in deployment configuration") - return func(context, *args, **kwargs) - - return wrapper_v1_dss_required diff --git a/monitoring/deployment_manager/actions/dss/v1/crdb.py b/monitoring/deployment_manager/actions/dss/v1/crdb.py deleted file mode 100644 index 33a4c773949..00000000000 --- a/monitoring/deployment_manager/actions/dss/v1/crdb.py +++ /dev/null @@ -1,121 +0,0 @@ -import base64 - -import cryptography.exceptions -import cryptography.hazmat.backends -import cryptography.hazmat.primitives.hashes -import cryptography.hazmat.primitives.serialization -import cryptography.x509 -import pem -import yaml -from kubernetes.client import V1Secret - -from monitoring.deployment_manager.actions.dss.v1.common import requires_v1_dss -from monitoring.deployment_manager.deploylib import crdb_sql -from monitoring.deployment_manager.deploylib.crdb_cluster_api import ClusterAPI -from monitoring.deployment_manager.deploylib.port_forwarding import ( - get_requests_session_for_pod, -) -from monitoring.deployment_manager.infrastructure import Context, deployment_action - - -def _public_key_bytes(public_key) -> bytes: - return public_key.public_bytes( - cryptography.hazmat.primitives.serialization.Encoding.PEM, - cryptography.hazmat.primitives.serialization.PublicFormat.SubjectPublicKeyInfo, - ) - - -@deployment_action("dss/crdb/print_ca_public_certs") -@requires_v1_dss -def print_ca_public_certs(context: Context): - """Print the accepted CA certificates accepted by the cluster.""" - backend = cryptography.hazmat.backends.default_backend() - - # Read the list of accepted CA certificates - ca_crt: V1Secret = context.clients.core.read_namespaced_secret( - name="cockroachdb.ca.crt", namespace=context.spec.dss.v1.namespace - ) - ca_crt_content = base64.b64decode(ca_crt.data["ca.crt"]) - certs = [ - cryptography.x509.load_pem_x509_certificate(str(c).encode("utf-8"), backend) - for c in pem.parse(ca_crt_content) - ] - context.log.msg("ca.crt:\n" + ca_crt_content.decode("utf-8")) - - # Read the private key to determine which certificate belongs to this instance - ca_key: V1Secret = context.clients.core.read_namespaced_secret( - name="cockroachdb.ca.key", namespace=context.spec.dss.v1.namespace - ) - ca_key_content = base64.b64decode(ca_key.data["ca.key"]).decode("utf-8") - private_key = cryptography.hazmat.primitives.serialization.load_pem_private_key( - ca_key_content.encode("utf-8"), password=None, backend=backend - ) - public_key_bytes = _public_key_bytes(private_key.public_key()) - - matches = [ - i - for i in range(len(certs)) - if public_key_bytes == _public_key_bytes(certs[i].public_key()) - ] - match_words = ["first", "second", "third"] + [f"{i}th" for i in range(4, 50)] - if not matches: - context.log.warn( - "This DSS instance's public key does not appear in any of the listed certificates" - ) - else: - context.log.msg( - "This DSS instance's public key matches the {} certificate{} in those above".format( - ", ".join(match_words[m] for m in matches), - "s" if len(matches) > 1 else "", - ) - ) - - -@deployment_action("dss/crdb/status") -@requires_v1_dss -def crdb_status(context: Context): - """Retrieve and print information about the CockroachDB cluster.""" - username, password = crdb_sql.get_monitoring_user( - context.clients.core, context.spec.dss.v1.namespace, context.spec.cluster.name - ) - pod_name = "cockroachdb-0" - pod_session, host_port = get_requests_session_for_pod( - pod_name, context.spec.dss.v1.namespace, 8080, context.clients.core - ) - cluster = ClusterAPI( - pod_session, - base_url=f"https://{host_port}/api/v2", - username=username, - password=password, - ) - up = cluster.is_up() - ready = cluster.is_ready() - source = "{} ({}, {})".format( - pod_name, "up" if up else "DOWN", "ready" if ready else "NOT READY" - ) - if up and ready: - nodes = cluster.get_nodes() - summary = dict() - for n in nodes: - k, v = n.summarize() - summary[k] = v - context.log.msg(f"{source} reports:\n" + yaml.dump(summary)) - else: - context.log.msg(f"{source} not ready to query nodes") - - -@deployment_action("dss/crdb/print_monitoring_user") -@requires_v1_dss -def print_monitoring_user(context: Context): - """Print the username and password of the CRDB monitoring user. - - This username and password can be used to view the debug console web UI on - one of the CRDB nodes, or to access the cluster API on one of the CRDB - nodes. Its access will expire 1-2 days after this action if no other - applicable deployment_manager action is taken. - """ - username, password = crdb_sql.get_monitoring_user( - context.clients.core, context.spec.dss.v1.namespace, context.spec.cluster.name - ) - context.log.msg(f"Username: {username} Password: {password}") - return diff --git a/monitoring/deployment_manager/actions/k8s.py b/monitoring/deployment_manager/actions/k8s.py deleted file mode 100644 index 65986498e53..00000000000 --- a/monitoring/deployment_manager/actions/k8s.py +++ /dev/null @@ -1,41 +0,0 @@ -from typing import cast - -import kubernetes - -from monitoring.deployment_manager.infrastructure import Context, deployment_action - - -@deployment_action("list_pods") -def list_pods(context: Context): - """List all Kubernetes pods""" - if not context.clients: - raise ValueError("Clients not initialized in context") - ret = cast( - kubernetes.client.V1PodList, - context.clients.core.list_pod_for_all_namespaces(watch=False), - ) - msg = "\n".join( - [ - f"{i.status.pod_ip}\t{i.metadata.namespace}\t{i.metadata.name}" - for i in ret.items or [] - ] - ) - context.log.msg("Pods:\n" + msg) - - -@deployment_action("list_ingress_controllers") -def list_ingress_controllers(context: Context): - """List all available ingress controllers""" - if not context.clients: - raise ValueError("Clients not initialized in context") - class_list = cast( - kubernetes.client.V1IngressClassList, - context.clients.networking.list_ingress_class(), - ) - msg = "\n".join( - [ - f"{c.metadata.name}\t{c.spec.controller}\t{c.spec.parameters}" - for c in class_list.items or [] - ] - ) - context.log.msg("Ingress controllers:\n" + msg) diff --git a/monitoring/deployment_manager/actions/test/README.md b/monitoring/deployment_manager/actions/test/README.md deleted file mode 100644 index 9dd8d3d0e81..00000000000 --- a/monitoring/deployment_manager/actions/test/README.md +++ /dev/null @@ -1,28 +0,0 @@ -## Contents - -This folder contains deployment_manager actions that relate to tests not meant to exist in any real InterUSS deployment. - -## hello_world - -The actions in this module relate to a minimal webserver. - -### Deployment - -To deploy a hello_world system, starting in the `deployment_manager` folder: - -`python3 deployment_manager.py test/hello_world/deploy examples/test.json` - -To tear down a hello_world system, starting in the `deployment_manager` folder: - -`python3 deployment_manager.py test/hello_world/destroy examples/test.json` - -### Usage - -Once deployed, the Service can be accessed directly with `minikube service webserver-service -n test`. This will forward a port on the development machine to the Service port, and open a browser to localhost:THAT_PORT. - -However, normal access to the system is usually provided via its ingress and is accomplished by running `minikube tunnel`. While the tunnel is running: - -* http://localhost or http://localhost:80 accesses the Ingress -* https://localhost (this sometimes works depending on browser) or https://localhost:443 (this should always work) access the Ingress -* http://localhost:8002 accesses the Service -* Port 8001 is not generally accessible outside the cluster, though the name of the pod can be discovered with `kubectl get pods -n test`, and then port 8001 from that pod can be forwarded to the development machine with, e.g., `kubectl -n test port-forward webserver-deployment-58f7879d9b-c2qqx 8001` (when this command is running, http://localhost:8001 in a browser will access the Pod directly). diff --git a/monitoring/deployment_manager/actions/test/__init__.py b/monitoring/deployment_manager/actions/test/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/actions/test/hello_world.py b/monitoring/deployment_manager/actions/test/hello_world.py deleted file mode 100644 index 768f10d1f4c..00000000000 --- a/monitoring/deployment_manager/actions/test/hello_world.py +++ /dev/null @@ -1,54 +0,0 @@ -from monitoring.deployment_manager.deploylib import namespaces, systems -from monitoring.deployment_manager.infrastructure import Context, deployment_action -from monitoring.deployment_manager.systems.test import hello_world -from monitoring.monitorlib.delay import sleep - - -@deployment_action("test/hello_world/deploy") -def deploy(context: Context) -> None: - """Bring up the hello_world system""" - namespace = hello_world.define_namespace(context.spec.test.v1.namespace) - resources = hello_world.define_resources() - - active_namespace = namespaces.upsert(context.clients.core, context.log, namespace) - systems.upsert_resources(resources, active_namespace, context.clients, context.log) - - context.log.msg( - "hello_world system deployment complete. Run `minikube tunnel` and then navigate to http://localhost in a browser to see the web content." - ) - - -@deployment_action("test/hello_world/destroy") -def destroy(context: Context) -> None: - """Tear down the hello_world system""" - namespace = namespaces.get( - context.clients.core, - context.log, - hello_world.define_namespace(context.spec.test.v1.namespace), - ) - if namespace is None: - context.log.warn( - f"Namespace `{context.spec.test.v1.namespace}` does not exist in `{context.spec.cluster.name}` cluster" - ) - return - - resources = hello_world.define_resources() - resources.reverse() - existing_resources = systems.get_resources( - resources, namespace, context.clients, context.log, context.spec.cluster.name - ) - - context.log.warn( - f"Destroying hello_world system in `{namespace.metadata.name}` namespace of `{context.spec.cluster.name}` cluster in 15 seconds..." - ) - sleep(15, "destruction of hello_world system may take a few seconds") - - systems.delete_resources( - existing_resources, namespace, context.clients, context.log - ) - - context.log.msg("Deleting namespace") - status = context.clients.core.delete_namespace(name=namespace.metadata.name) - context.log.msg("Namespace deleted", name=status.message) - - context.log.msg("hello_world system removal complete") diff --git a/monitoring/deployment_manager/deploylib/__init__.py b/monitoring/deployment_manager/deploylib/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/deploylib/common_k8s.py b/monitoring/deployment_manager/deploylib/common_k8s.py deleted file mode 100644 index 023de18acbf..00000000000 --- a/monitoring/deployment_manager/deploylib/common_k8s.py +++ /dev/null @@ -1,52 +0,0 @@ -from collections.abc import Callable -from typing import Any - -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import comparisons - - -def get_resource( - list_resources: Callable[[], Any], - log: BoundLogger, - resource_type: str, - resource_name: str, -) -> Any | None: - log.msg(f"Checking for existing {resource_type}", name=resource_name) - resource_list = list_resources() - matching_resources = [ - d for d in resource_list.items if d.metadata.name == resource_name - ] - if len(matching_resources) > 2: - raise ValueError( - f"Found {len(matching_resources)} {resource_type}s matching `{resource_name}`" - ) - if not matching_resources: - return None - return matching_resources[0] - - -def upsert_resource( - existing_resource: Any | None, - target_resource: Any, - log: BoundLogger, - resource_type: str, - create: Callable[[], Any], - patch: Callable[[], Any], -) -> Any: - if existing_resource is not None: - if comparisons.specs_are_the_same(existing_resource, target_resource): - log.msg( - f"Existing {resource_type} does not need to be updated", - name=existing_resource.metadata.name, - ) - new_resource = existing_resource - else: - log.msg(f"Updating existing {resource_type}") - new_resource = patch() - log.msg(f"Updated {resource_type}", name=new_resource.metadata.name) - else: - log.msg(f"Creating new {resource_type}") - new_resource = create() - log.msg(f"Created {resource_type}", name=new_resource.metadata.name) - return new_resource diff --git a/monitoring/deployment_manager/deploylib/comparisons.py b/monitoring/deployment_manager/deploylib/comparisons.py deleted file mode 100644 index 268d6086cc5..00000000000 --- a/monitoring/deployment_manager/deploylib/comparisons.py +++ /dev/null @@ -1,118 +0,0 @@ -from collections.abc import Callable -from typing import Any - -from kubernetes.client import ( - V1Deployment, - V1Ingress, - V1Namespace, - V1ObjectMeta, - V1Secret, - V1Service, -) - -_special_comparisons: dict[type, Callable[[Any, Any], bool]] = {} - - -def specs_are_the_same( - obj1: Any, obj2: Any, field_paths: list[str] | None = None -) -> bool: - """Determine if the specifications for two Kubernetes objects are equivalent - - Only the relevant parts of the two objects will be compared to see if they - are describing the same specification. The relevant descendant fields are - described by field_paths (if specified). If field_paths is not specified or - empty, then first the _special_comparisons registry will be checked to see - if there is a custom comparison function for obj1's class type. If not, - field_paths will be set to the list of keys in obj1.attribute_map if it - exists. Finally, obj1 and obj2 will be checked for simple equality if none - of the above is applicable. - - :param obj1: Kubernetes object or field value - :param obj2: Kubernetes object or field value - :param field_paths: Specific descendant paths to check, if specified - :return: Whether the specs described by the two objects are the same - """ - if not field_paths: - special_are_the_same = _special_comparisons.get(obj1.__class__, None) - if special_are_the_same is not None: - return special_are_the_same(obj1, obj2) - elif hasattr(obj1, "attribute_map"): - return specs_are_the_same( - obj1, obj2, list(getattr(obj1, "attribute_map").keys) - ) - elif isinstance(obj1, dict) and isinstance(obj2, dict): - return all(obj2[k] == v for k, v in obj1.items()) and all( - obj1[k] == v for k, v in obj2.items() - ) - elif isinstance(obj1, list) and isinstance(obj2, list): - return all(v1 == v2 for v1, v2 in zip(obj1, obj2)) - else: - return obj1 == obj2 - - sub_paths: dict[str, list[str] | None] = {} - for field_path in field_paths: - parts = field_path.split(".") - if len(parts) == 1: - if parts[0] in sub_paths: - raise ValueError( - f"Cannot compare {parts[0]} and its subfield {field_path}" - ) - sub_paths[parts[0]] = None - else: - sub_fields = sub_paths.get(parts[0], []) - sub_fields.append(".".join(parts[1:])) - sub_paths[parts[0]] = sub_fields - - for field_name, paths in sub_paths.items(): - if not hasattr(obj1, field_name) and not hasattr(obj2, field_name): - continue - elif hasattr(obj1, field_name) and hasattr(obj2, field_name): - value1 = getattr(obj1, field_name) - value2 = getattr(obj2, field_name) - if not specs_are_the_same(value1, value2, paths): - return False - else: - return False - - return True - - -def _special_comparison(type: type): - def decorator_declare_comparison(compare: Callable[[Any, Any], bool]): - global _special_comparisons - _special_comparisons[type] = compare - return compare - - return decorator_declare_comparison - - -@_special_comparison(V1Namespace) -def _v1namespace_are_the_same(namespace1: V1Namespace, namespace2: V1Namespace) -> bool: - return specs_are_the_same(namespace1, namespace2, ["metadata"]) - - -@_special_comparison(V1ObjectMeta) -def _v1objectmeta_are_the_same(meta1: V1ObjectMeta, meta2: V1ObjectMeta) -> bool: - return specs_are_the_same( - meta1, meta2, ["annotations", "labels", "name", "namespace"] - ) - - -@_special_comparison(V1Deployment) -def _v1deployment_are_the_same(dep1: V1Deployment, dep2: V1Deployment) -> bool: - return specs_are_the_same(dep1, dep2, ["metadata", "spec"]) - - -@_special_comparison(V1Service) -def _v1service_are_the_same(svc1: V1Service, svc2: V1Service) -> bool: - return specs_are_the_same(svc1, svc2, ["metadata", "spec"]) - - -@_special_comparison(V1Ingress) -def _v1ingress_are_the_same(ingress1: V1Ingress, ingress2: V1Ingress) -> bool: - return specs_are_the_same(ingress1, ingress2, ["metadata", "spec"]) - - -@_special_comparison(V1Secret) -def _v1secret_are_the_same(secret1: V1Secret, secret2: V1Secret) -> bool: - return specs_are_the_same(secret1, secret2, ["metadata", "data"]) diff --git a/monitoring/deployment_manager/deploylib/conversion.py b/monitoring/deployment_manager/deploylib/conversion.py deleted file mode 100644 index 4d325161779..00000000000 --- a/monitoring/deployment_manager/deploylib/conversion.py +++ /dev/null @@ -1,37 +0,0 @@ -import re -from typing import Any - -import kubernetes - - -def to_openapi_object(value: Any, type_name: str) -> Any: - primitives = {"int", "str"} - - # Do not convert primitives - if type_name in primitives: - return value - - # Convert dicts - dict_regex = r"dict\(([^,]+), ([^,]+)\)" - m = re.fullmatch(dict_regex, type_name) - if m: - return { - to_openapi_object(k, m.group(1)): to_openapi_object(v, m.group(2)) - for k, v in value.items() - } - - # Convert lists - list_regex = r"list\[([^]]+)\]" - m = re.fullmatch(list_regex, type_name) - if m: - return [to_openapi_object(v, m.group(1)) for v in value] - - # Convert OpenAPI objects - api_type = getattr(kubernetes.client, type_name) - kwargs = {} - api_arg_names = api_type.attribute_map - for arg_name, arg_type_name in api_type.openapi_types.items(): - api_arg_name = api_arg_names[arg_name] - if api_arg_name in value: - kwargs[arg_name] = to_openapi_object(value[api_arg_name], arg_type_name) - return api_type(**kwargs) diff --git a/monitoring/deployment_manager/deploylib/crdb_cluster_api.py b/monitoring/deployment_manager/deploylib/crdb_cluster_api.py deleted file mode 100644 index a9bd32bd64c..00000000000 --- a/monitoring/deployment_manager/deploylib/crdb_cluster_api.py +++ /dev/null @@ -1,151 +0,0 @@ -import math - -import arrow -import requests -from implicitdict import ImplicitDict - - -class Address(ImplicitDict): - network_field: str - address_field: str - - -class ServerVersion(ImplicitDict): - major_val: int - minor_val: int - patch: int - internal: int - - -class Locality(ImplicitDict): - tiers: list[dict] - - -class Node(ImplicitDict): - node_id: int - address: Address - locality: Locality - ServerVersion: ServerVersion - build_tag: str - started_at: int - cluster_name: str - sql_address: Address - metrics: dict - total_system_memory: int - num_cpus: int - updated_at: int - liveness_status: int - - def summarize(self) -> tuple[str, dict[str, str]]: - key = f"Node {self.node_id} ({self.address.address_field})" - t0 = arrow.get(math.floor(self.started_at / 1e9)) - values = { - "locality": " ".join( - "{}:{}".format(t["key"], t["value"]) for t in self.locality.tiers - ), - "status": "Running {} since {}, liveness {}".format( - self.build_tag, t0.to("local").isoformat(), self.liveness_status - ), - } - return key, values - - -class ClusterAPI: - """Wrapper for retrieving CockroachDB cluster information. - - API: https://www.cockroachlabs.com/docs/api/cluster/v2 - """ - - def __init__( - self, - session: requests.Session, - base_url: str = "https://localhost:8080/api/v2", - username: str | None = None, - password: str | None = None, - ): - self._session = session - self._base_url = base_url - self._username = username - self._password = password - self._session_auth = None - - def __del__(self): - self.log_out() - - def is_ready(self) -> bool: - resp = self._session.get(f"{self._base_url}/health/?ready=true") - if resp.status_code == 200: - return True - elif resp.status_code == 500: - return False - else: - raise ValueError( - "Call to {} returned unexpected status code {}: {}".format( - resp.url, resp.status_code, resp.content.decode("utf-8") - ) - ) - - def is_up(self) -> bool: - resp = self._session.get(f"{self._base_url}/health/") - if resp.status_code == 200: - return True - elif resp.status_code == 500: - return False - else: - raise ValueError( - "Call to {} returned unexpected status code {}: {}".format( - resp.url, resp.status_code, resp.content.decode("utf-8") - ) - ) - - def log_in(self) -> str: - if self._username is None: - raise ValueError( - f"Cannot log in to CockroachDB cluster at {self._base_url} when username is not specified" - ) - if self._password is None: - raise ValueError( - f"Cannot log in to CockroachDB cluster at {self._base_url} when password is not specified" - ) - resp = self._session.post( - f"{self._base_url}/login/", - data={"username": self._username, "password": self._password}, - ) - resp.raise_for_status() - session = resp.json().get("session", None) - if session is None: - raise ValueError( - "Invalid CockroachDB cluster response: `session` not specified: {}".format( - resp.content.decode("utf-8") - ) - ) - self._session_auth = session - return session - - def _get_headers(self) -> dict[str, str]: - if self._session_auth is None: - self.log_in() - return {"X-Cockroach-API-Session": self._session_auth} - - def log_out(self) -> None: - if self._session_auth is None: - return - resp = self._session.post( - f"{self._base_url}/logout/", headers=self._get_headers() - ) - resp.raise_for_status() - self._session_auth = None - - def get_nodes(self) -> list[Node]: - resp = self._session.get( - f"{self._base_url}/nodes/", headers=self._get_headers() - ) - resp.raise_for_status() - nodes = resp.json().get("nodes", None) - if nodes is None: - raise ValueError( - "Invalid CockroachDB cluster response: `nodes` not specified: {}".format( - resp.content.decode("utf-8") - ) - ) - return [ImplicitDict.parse(n, Node) for n in nodes] diff --git a/monitoring/deployment_manager/deploylib/crdb_sql.py b/monitoring/deployment_manager/deploylib/crdb_sql.py deleted file mode 100644 index 65c9b1758c9..00000000000 --- a/monitoring/deployment_manager/deploylib/crdb_sql.py +++ /dev/null @@ -1,100 +0,0 @@ -import datetime -import hashlib -import random -from dataclasses import dataclass - -import kubernetes.stream -from kubernetes import client as k8s - - -@dataclass -class User: - username: str - options: list[str] - member_of: list[str] - - -def execute_sql(client: k8s.CoreV1Api, namespace: str, sql_commands: list[str]) -> str: - """Execute the specfied sql_commands directly on a CRDB node. - - :param client: Kubernetes core client which can access the CRDB node - :param namespace: Namespace in which an accessible CRDB node is located - :param sql_commands: SQL commands to be executed (no semicolons) - :return: stdout of console from executing `cockroach sql` - """ - exec_command = ["./cockroach", "sql", "--certs-dir=cockroach-certs/"] - exec_command += ["--execute=" + cmd for cmd in sql_commands] - resp = kubernetes.stream.stream( - client.connect_get_namespaced_pod_exec, - "cockroachdb-0", - namespace, - command=exec_command, - stderr=True, - stdin=False, - stdout=True, - tty=False, - ) - if "Failed running" in resp: - raise ValueError("SQL error: " + resp) - return resp - - -def list_users(client: k8s.CoreV1Api, namespace: str) -> list[User]: - lines = execute_sql(client, namespace, ["SHOW USERS"]).split("\n") - lines = [line for line in lines[1:] if line] - users = [] - for line in lines: - username, options_text, member_of_text = [ - col.strip() for col in line.split("\t") - ] - options = [opt.strip() for opt in options_text.split(",")] - member_of = [group.strip() for group in member_of_text[1:-1].split(",")] - users.append(User(username=username, options=options, member_of=member_of)) - return users - - -def get_monitoring_user( - client: k8s.CoreV1Api, namespace: str, cluster_name: str -) -> tuple[str, str]: - """Get the username and password for a CRDB user intended for monitoring. - - Whenever this routine is called, it sets the validity of the user to 1-2 - days beyond the current time. To continue to use the user after that time, - this routine must be called again. - - :param client: Kubernetes core client which can access the CRDB node - :param namespace: Namespace in which an accessible CRDB node is located - :param cluster_name: Name of Kubernetes cluster in which this user will be operating - :return: - * Username - * Password - """ - # Create username according to cluster_name - prefix = "monitoring_user_" - suffix = hashlib.md5(cluster_name.encode("utf-8")).hexdigest()[-8:] - username = prefix + suffix - - # Create a new password - random.Random() - password = "".join( - random.choice( - 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=_+[]\\{}|;:",./<>?' - ) - for _ in range(32) - ) - - # Compute validity - valid_until = ( - datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=2) - ).isoformat() - execute_sql( - client, - namespace, - [ - f"CREATE USER IF NOT EXISTS {username}", - f"GRANT admin TO {username}", - f"ALTER USER {username} WITH LOGIN PASSWORD '{password}' VALID UNTIL '{valid_until}'", - ], - ) - - return username, password diff --git a/monitoring/deployment_manager/deploylib/deployments.py b/monitoring/deployment_manager/deploylib/deployments.py deleted file mode 100644 index 883338a1f31..00000000000 --- a/monitoring/deployment_manager/deploylib/deployments.py +++ /dev/null @@ -1,33 +0,0 @@ -from kubernetes.client import AppsV1Api, V1Deployment, V1Namespace -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import common_k8s - - -def get( - client: AppsV1Api, log: BoundLogger, namespace: V1Namespace, dep: V1Deployment -) -> V1Deployment | None: - return common_k8s.get_resource( - lambda: client.list_namespaced_deployment(namespace=namespace.metadata.name), - log, - "deployment", - dep.metadata.name, - ) - - -def upsert( - client: AppsV1Api, log: BoundLogger, namespace: V1Namespace, dep: V1Deployment -) -> V1Deployment: - existing_dep = get(client, log, namespace, dep) - return common_k8s.upsert_resource( - existing_dep, - dep, - log, - "deployment", - lambda: client.create_namespaced_deployment( - body=dep, namespace=namespace.metadata.name - ), - lambda: client.patch_namespaced_deployment( - existing_dep.metadata.name, namespace.metadata.name, dep - ), - ) diff --git a/monitoring/deployment_manager/deploylib/ingresses.py b/monitoring/deployment_manager/deploylib/ingresses.py deleted file mode 100644 index 8022586bf0f..00000000000 --- a/monitoring/deployment_manager/deploylib/ingresses.py +++ /dev/null @@ -1,39 +0,0 @@ -from kubernetes.client import NetworkingV1Api, V1Ingress, V1Namespace -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import common_k8s - - -def get( - client: NetworkingV1Api, - log: BoundLogger, - namespace: V1Namespace, - ingress: V1Ingress, -) -> V1Ingress | None: - return common_k8s.get_resource( - lambda: client.list_namespaced_ingress(namespace=namespace.metadata.name), - log, - "ingress", - ingress.metadata.name, - ) - - -def upsert( - client: NetworkingV1Api, - log: BoundLogger, - namespace: V1Namespace, - ingress: V1Ingress, -) -> V1Ingress: - existing_ingress = get(client, log, namespace, ingress) - return common_k8s.upsert_resource( - existing_ingress, - ingress, - log, - "ingress", - lambda: client.create_namespaced_ingress( - body=ingress, namespace=namespace.metadata.name - ), - lambda: client.patch_namespaced_ingress( - existing_ingress.metadata.name, namespace.metadata.name, ingress - ), - ) diff --git a/monitoring/deployment_manager/deploylib/namespaces.py b/monitoring/deployment_manager/deploylib/namespaces.py deleted file mode 100644 index b52b0746737..00000000000 --- a/monitoring/deployment_manager/deploylib/namespaces.py +++ /dev/null @@ -1,23 +0,0 @@ -from kubernetes.client import CoreV1Api, V1Namespace -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import common_k8s - - -def get( - client: CoreV1Api, log: BoundLogger, namespace: V1Namespace -) -> V1Namespace | None: - return common_k8s.get_resource( - lambda: client.list_namespace(), log, "namespace", namespace.metadata.name - ) - - -def upsert(client: CoreV1Api, log: BoundLogger, namespace: V1Namespace) -> V1Namespace: - return common_k8s.upsert_resource( - get(client, log, namespace), - namespace, - log, - "namespace", - lambda: client.create_namespace(body=namespace), - lambda: client.patch_namespace(namespace.metadata.name, body=namespace), - ) diff --git a/monitoring/deployment_manager/deploylib/port_forwarding.py b/monitoring/deployment_manager/deploylib/port_forwarding.py deleted file mode 100644 index baffbd642a9..00000000000 --- a/monitoring/deployment_manager/deploylib/port_forwarding.py +++ /dev/null @@ -1,163 +0,0 @@ -from http.client import HTTPConnection - -import kubernetes.stream -import requests -import urllib3 -import urllib3.connection -from kubernetes.client import CoreV1Api -from requests.adapters import HTTPAdapter -from urllib3 import HTTPConnectionPool, HTTPSConnectionPool, PoolManager - -urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) - - -def _get_session_for_socket(sock, host: str, port: int) -> requests.Session: - sock.setblocking(True) - s = requests.Session() - s.verify = False - http_adapter = SocketHTTPAdapter(sock, host, port) - s.mount("http://", http_adapter) - s.mount("https://", http_adapter) - return s - - -def get_requests_session_for_pod( - pod_name: str, namespace: str, port: int, client: CoreV1Api -) -> tuple[requests.Session, str]: - """Make a Session to connect to the specified port on the named pod. - - Retrieves a requests Session that will send http(s) queries to the specified - port on the specified pod. Only requests sent to the returned host:port - combination will be sent to the pod; other requests will result in errors. - - :param pod_name: Name of Kubernetes pod to connect to - :param namespace: Kubernetes namespace in which the pod is located - :param port: Pod's port to connect to - :param client: Kubernetes client that can access the Kubernetes cluster - :return: - * requests.Session that will can be used to make HTTP(S) requests - * host:port combination to which requests should be addressed - """ - pf = kubernetes.stream.portforward( - client.connect_get_namespaced_pod_portforward, - pod_name, - namespace, - ports=str(port), - ) - host = f"{pod_name}.pod.{namespace}" - return _get_session_for_socket(pf.socket(port), host, port), f"{host}:{port}" - - -# ==== Custom requests handlers to inject/use an explicitly-provided socket ==== - - -class SocketHTTPAdapter(HTTPAdapter): - def __init__(self, sock, host: str, port: int, *args, **kwargs): - self._socket = sock - self._host = host - self._port = port - super().__init__(*args, **kwargs) - - def init_poolmanager( - self, - connections, - maxsize, - block=requests.adapters.DEFAULT_POOLBLOCK, - **pool_kwargs, - ): - """Overrides method in base class""" - self.poolmanager = SocketPoolManager( - self._socket, self._host, self._port, num_pools=connections, maxsize=maxsize - ) - - -class SocketPoolManager(PoolManager): - def __init__(self, sock, host: str, port: int, *args, **kwargs): - self._socket = sock - self._host = host - self._port = port - super().__init__(*args, **kwargs) - - def _new_pool(self, scheme, host, port, request_context=None): - """Overrides method in base class""" - # return super(SocketPoolManager, self)._new_pool(scheme, host, port, request_context) - if host == self._host and port == self._port: - if scheme == "http": - return SocketHTTPConnectionPool( - self._socket, host, port, **self.connection_pool_kw - ) - elif scheme == "https": - return SocketHTTPSConnectionPool( - self._socket, host, port, **self.connection_pool_kw - ) - raise ValueError( - f"{host}:{port} is not supported by SocketPoolManager intended for {self._host}:{self._port}" - ) - - -class SocketHTTPConnectionPool(HTTPConnectionPool): - def __init__(self, sock, *args, **kwargs): - self._socket = sock - super().__init__(*args, **kwargs) - - def _new_conn(self): - """Overrides method in base class""" - self.num_connections += 1 - return SocketHTTPConnection( - self._socket, - host=self.host, - port=self.port, - timeout=self.timeout.connect_timeout, - **self.conn_kw, - ) - - -class SocketHTTPConnection(HTTPConnection): - def __init__(self, sock, *args, **kwargs): - self._socket = sock - super().__init__(*args, **kwargs) - - def connect(self): - """Overrides method in base class""" - self.sock = self._socket - if self._tunnel_host: - self._tunnel() - - -class SocketHTTPSConnectionPool(HTTPSConnectionPool): - def __init__(self, sock, *args, **kwargs): - self._socket = sock - kwargs["assert_hostname"] = False - super().__init__(*args, **kwargs) - - def _new_conn(self): - """Overrides method in base class""" - self.num_connections += 1 - - actual_host = self.host - actual_port = self.port - if self.proxy is not None: - actual_host = self.proxy.host - actual_port = self.proxy.port - - conn = SocketHTTPSConnection( - self._socket, - host=actual_host, - port=actual_port, - timeout=self.timeout.connect_timeout, - cert_file=self.cert_file, - key_file=self.key_file, - **self.conn_kw, - ) - - return self._prepare_conn(conn) - - -class SocketHTTPSConnection(urllib3.connection.HTTPSConnection): - def __init__(self, sock, *args, **kwargs): - self._socket = sock - super().__init__(*args, **kwargs) - - def _new_conn(self): - """Overrides method in base class""" - return self._socket diff --git a/monitoring/deployment_manager/deploylib/secrets.py b/monitoring/deployment_manager/deploylib/secrets.py deleted file mode 100644 index 08de907b47b..00000000000 --- a/monitoring/deployment_manager/deploylib/secrets.py +++ /dev/null @@ -1,33 +0,0 @@ -from kubernetes.client import CoreV1Api, V1Namespace, V1Secret -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import common_k8s - - -def get( - client: CoreV1Api, log: BoundLogger, namespace: V1Namespace, name: str -) -> V1Secret | None: - return common_k8s.get_resource( - lambda: client.list_namespaced_secret(namespace=namespace.metadata.name), - log, - "secret", - name, - ) - - -def upsert( - client: CoreV1Api, log: BoundLogger, namespace: V1Namespace, secret: V1Secret -) -> V1Secret: - existing_secret = get(client, log, namespace, secret.metadata.name) - return common_k8s.upsert_resource( - existing_secret, - secret, - log, - "secret", - lambda: client.create_namespaced_secret( - body=secret, namespace=namespace.metadata.name - ), - lambda: client.patch_namespaced_secret( - existing_secret.metadata.name, namespace.metadata.name, secret - ), - ) diff --git a/monitoring/deployment_manager/deploylib/services.py b/monitoring/deployment_manager/deploylib/services.py deleted file mode 100644 index 4f4e689c437..00000000000 --- a/monitoring/deployment_manager/deploylib/services.py +++ /dev/null @@ -1,33 +0,0 @@ -from kubernetes.client import CoreV1Api, V1Namespace, V1Service -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import common_k8s - - -def get( - client: CoreV1Api, log: BoundLogger, namespace: V1Namespace, svc: V1Service -) -> V1Service | None: - return common_k8s.get_resource( - lambda: client.list_namespaced_service(namespace=namespace.metadata.name), - log, - "service", - svc.metadata.name, - ) - - -def upsert( - client: CoreV1Api, log: BoundLogger, namespace: V1Namespace, svc: V1Service -) -> V1Service: - existing_svc = get(client, log, namespace, svc) - return common_k8s.upsert_resource( - existing_svc, - svc, - log, - "service", - lambda: client.create_namespaced_service( - body=svc, namespace=namespace.metadata.name - ), - lambda: client.patch_namespaced_service( - existing_svc.metadata.name, namespace.metadata.name, svc - ), - ) diff --git a/monitoring/deployment_manager/deploylib/systems.py b/monitoring/deployment_manager/deploylib/systems.py deleted file mode 100644 index bb56c27be01..00000000000 --- a/monitoring/deployment_manager/deploylib/systems.py +++ /dev/null @@ -1,106 +0,0 @@ -from typing import Any - -from kubernetes.client import V1Deployment, V1Ingress, V1Namespace, V1Service -from structlog import BoundLogger - -from monitoring.deployment_manager.deploylib import ( - deployments, - ingresses, - namespaces, - services, -) -from monitoring.deployment_manager.infrastructure import Clients - - -def upsert_resources( - target_resources: list[Any], - namespace: V1Namespace, - clients: Clients, - log: BoundLogger, -) -> None: - for target_resource in target_resources: - if target_resource.__class__ == V1Deployment: - deployments.upsert(clients.apps, log, namespace, target_resource) - elif target_resource.__class__ == V1Ingress: - ingresses.upsert(clients.networking, log, namespace, target_resource) - elif target_resource.__class__ == V1Namespace: - namespaces.upsert(clients.core, log, target_resource) - elif target_resource.__class__ == V1Service: - services.upsert(clients.core, log, namespace, target_resource) - else: - raise NotImplementedError( - f"Upserting {target_resource.__class__} is not yet supported" - ) - - -def get_resources( - target_resources: list[Any], - namespace: V1Namespace, - clients: Clients, - log: BoundLogger, - cluster_name: str, -) -> list[Any]: - existing_resources = [] - for target_resource in target_resources: - if target_resource.__class__ == V1Deployment: - existing_resource = deployments.get( - clients.apps, log, namespace, target_resource - ) - elif target_resource.__class__ == V1Ingress: - existing_resource = ingresses.get( - clients.networking, log, namespace, target_resource - ) - elif target_resource.__class__ == V1Namespace: - existing_resource = namespaces.get(clients.core, log, target_resource) - elif target_resource.__class__ == V1Service: - existing_resource = services.get( - clients.core, log, namespace, target_resource - ) - else: - raise NotImplementedError( - f"Getting {target_resource.__class__} is not yet supported" - ) - - if existing_resource is None: - log.warn( - f"No existing {target_resource.metadata.name} {target_resource.__class__.__name__} found in `{namespace.metadata.name}` namespace of `{cluster_name}` cluster" - ) - existing_resources.append(existing_resource) - return existing_resources - - -def delete_resources( - existing_resources: list[Any], - namespace: V1Namespace, - clients: Clients, - log: BoundLogger, -): - for existing_resource in existing_resources: - if existing_resource is None: - pass - elif existing_resource.__class__ == V1Deployment: - log.msg("Deleting deployment") - status = clients.apps.delete_namespaced_deployment( - name=existing_resource.metadata.name, namespace=namespace.metadata.name - ) - log.msg("Deployment deleted", message=status.message) - elif existing_resource.__class__ == V1Ingress: - log.msg("Deleting ingress") - status = clients.networking.delete_namespaced_ingress( - name=existing_resource.metadata.name, namespace=namespace.metadata.name - ) - log.msg("Ingress deleted", message=status.message) - elif existing_resource.__class__ == V1Namespace: - log.msg("Deleting namespace") - status = clients.core.delete_namespace(name=namespace.metadata.name) - log.msg("Namespace deleted", name=status.message) - elif existing_resource.__class__ == V1Service: - log.msg("Deleting service") - svc = clients.core.delete_namespaced_service( - name=existing_resource.metadata.name, namespace=namespace.metadata.name - ) - log.msg("Service deleted", message=svc.metadata.name) - else: - raise NotImplementedError( - f"Deleting {existing_resource.__class__} is not yet supported" - ) diff --git a/monitoring/deployment_manager/deployment_manager.py b/monitoring/deployment_manager/deployment_manager.py deleted file mode 100755 index 4593fba3333..00000000000 --- a/monitoring/deployment_manager/deployment_manager.py +++ /dev/null @@ -1,69 +0,0 @@ -#!env/bin/python3 - -import argparse -import json -import os -import sys - -from implicitdict import ImplicitDict - -from monitoring.deployment_manager import actions, infrastructure -from monitoring.deployment_manager.infrastructure import make_context -from monitoring.deployment_manager.systems.configuration import DeploymentSpec -from monitoring.monitorlib.inspection import import_submodules - - -def _parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="Manage an InterUSS deployment") - - actions = parser.add_subparsers( - title="available deployment actions", dest="action", metavar="ACTION" - ) - for name, func in infrastructure.actions.items(): - actions.add_parser(name, help=func.__doc__) - - parser.add_argument( - "deployment_spec", metavar="SPEC", type=str, help="specification for deployment" - ) - - return parser.parse_args() - - -def main() -> int: - # Import all submodules from the `actions` module so we can find all actions - import_submodules(actions) - - # Parse arguments - args = _parse_args() - - # Retrieve action function - action_method = infrastructure.actions.get(args.action, None) - if action_method is None: - raise ValueError(f"Could not find definition for action `{args.action}`") - - # Parse deployment spec - with open(args.deployment_spec) as f: - spec = ImplicitDict.parse(json.load(f), DeploymentSpec) - original_spec = json.dumps(spec) - context = make_context(spec) - - # Execute action - context.log.msg( - "Executing action", action=args.action, spec_file=args.deployment_spec - ) - action_method(context) - - # Check if the deployment spec was updated - new_spec = json.dumps(context.spec) - if new_spec != original_spec: - context.log.msg( - f"Deployment spec updated; writing changes to {args.deployment_spec}" - ) - with open(args.deployment_spec, "w") as f: - json.dump(context.spec, f, indent=2) - - return os.EX_OK - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/monitoring/deployment_manager/examples/test.json b/monitoring/deployment_manager/examples/test.json deleted file mode 100644 index fe0dbbd0ba3..00000000000 --- a/monitoring/deployment_manager/examples/test.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "cluster": { - "name": "minikube" - }, - "test": { - "v1": {} - } -} diff --git a/monitoring/deployment_manager/infrastructure.py b/monitoring/deployment_manager/infrastructure.py deleted file mode 100644 index db3224a655b..00000000000 --- a/monitoring/deployment_manager/infrastructure.py +++ /dev/null @@ -1,66 +0,0 @@ -from collections.abc import Callable -from dataclasses import dataclass - -import kubernetes -import structlog - -from monitoring.deployment_manager.systems.configuration import DeploymentSpec - - -@dataclass -class Clients: - core: kubernetes.client.CoreV1Api - apps: kubernetes.client.AppsV1Api - networking: kubernetes.client.NetworkingV1Api - - -@dataclass -class Context: - spec: DeploymentSpec - log: structlog.BoundLogger - clients: Clients | None - - -def make_context(spec: DeploymentSpec): - if spec.cluster is not None: - contexts, active_context = kubernetes.config.list_kube_config_contexts() - if not contexts: - raise ValueError("Cannot find any context in kube-config file.") - matching_contexts = [ - c["name"] for c in contexts if c["name"] == spec.cluster.name - ] - if not matching_contexts: - raise ValueError( - f"Cannot find definition for context `{spec.cluster.name}` in kube-config file" - ) - if len(matching_contexts) > 1: - raise ValueError( - f"Found multiple context definitions with the name `{spec.cluster.name}` in kube-config file" - ) - - api_client = kubernetes.config.new_client_from_config( - context=matching_contexts[0] - ) - clients = Clients( - core=kubernetes.client.CoreV1Api(api_client=api_client), - apps=kubernetes.client.AppsV1Api(api_client=api_client), - networking=kubernetes.client.NetworkingV1Api(api_client=api_client), - ) - else: - clients = None - - log = structlog.get_logger() - - return Context(spec=spec, log=log, clients=clients) - - -actions: dict[str, Callable[[Context], None]] = {} - - -def deployment_action(name: str): - def decorator_declare_action(action: Callable[[Context], None]): - global actions - actions[name] = action - return action - - return decorator_declare_action diff --git a/monitoring/deployment_manager/systems/README.md b/monitoring/deployment_manager/systems/README.md deleted file mode 100644 index f0373bb5f27..00000000000 --- a/monitoring/deployment_manager/systems/README.md +++ /dev/null @@ -1,7 +0,0 @@ -## Purpose - -This folder contains models of systems that [actions](../actions) can be performed on. - -## Links - -* [Kubernetes API classes](https://github.com/kubernetes-client/python/tree/master/kubernetes/docs) diff --git a/monitoring/deployment_manager/systems/__init__.py b/monitoring/deployment_manager/systems/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/systems/configuration.py b/monitoring/deployment_manager/systems/configuration.py deleted file mode 100644 index d149630efbd..00000000000 --- a/monitoring/deployment_manager/systems/configuration.py +++ /dev/null @@ -1,24 +0,0 @@ -from implicitdict import ImplicitDict, Optional - -from monitoring.deployment_manager.systems.dss.configuration import DSS -from monitoring.deployment_manager.systems.test.configuration import Test - - -class KubernetesCluster(ImplicitDict): - name: str - """Name of the Kubernetes cluster containing this deployment. - - Contained in the NAME column of the response to - `kubectl config get-contexts`. - """ - - -class DeploymentSpec(ImplicitDict): - cluster: Optional[KubernetesCluster] - """Definition of Kubernetes cluster containing this deployment.""" - - test: Optional[Test] - """Test systems in this deployment.""" - - dss: Optional[DSS] - """DSS instance in this deployment.""" diff --git a/monitoring/deployment_manager/systems/dss/__init__.py b/monitoring/deployment_manager/systems/dss/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/systems/dss/configuration.py b/monitoring/deployment_manager/systems/dss/configuration.py deleted file mode 100644 index b9aebdc459b..00000000000 --- a/monitoring/deployment_manager/systems/dss/configuration.py +++ /dev/null @@ -1,12 +0,0 @@ -from implicitdict import ImplicitDict - -from monitoring.deployment_manager.systems.dss.v1.configuration import V1DSS - - -class DSS(ImplicitDict): - """Definition of a DSS instance. - - Only one of the fields below should be populated. - """ - - v1: V1DSS diff --git a/monitoring/deployment_manager/systems/dss/v1/__init__.py b/monitoring/deployment_manager/systems/dss/v1/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/systems/dss/v1/configuration.py b/monitoring/deployment_manager/systems/dss/v1/configuration.py deleted file mode 100644 index c829482369d..00000000000 --- a/monitoring/deployment_manager/systems/dss/v1/configuration.py +++ /dev/null @@ -1,13 +0,0 @@ -from implicitdict import ImplicitDict - - -class V1DSS(ImplicitDict): - """Definition of a version 1 DSS instance. - - A v1 DSS system is a DSS instance consisting of CRDB nodes, and core-service - workers, initially defined with Jsonnet in build/deploy to be applied to a - Kubernetes cluster with Tanka. - """ - - namespace: str = "default" - """Namespace in which all DSS components are located""" diff --git a/monitoring/deployment_manager/systems/test/__init__.py b/monitoring/deployment_manager/systems/test/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/monitoring/deployment_manager/systems/test/configuration.py b/monitoring/deployment_manager/systems/test/configuration.py deleted file mode 100644 index 3c2b90bf9ac..00000000000 --- a/monitoring/deployment_manager/systems/test/configuration.py +++ /dev/null @@ -1,9 +0,0 @@ -from implicitdict import ImplicitDict, Optional - - -class TestV1(ImplicitDict): - namespace: str = "test" - - -class Test(ImplicitDict): - v1: Optional[TestV1] diff --git a/monitoring/deployment_manager/systems/test/hello_world.py b/monitoring/deployment_manager/systems/test/hello_world.py deleted file mode 100644 index 60ad9df200c..00000000000 --- a/monitoring/deployment_manager/systems/test/hello_world.py +++ /dev/null @@ -1,117 +0,0 @@ -from typing import Any - -from kubernetes import client as k8s - -DEPLOYMENT_NAME = "webserver-deployment" -CONTAINER_PORT = 8001 -APP_NAME = "webbserver-app" - -SERVICE_NAME = "webserver-service" -SERVICE_PORT = 8002 -SERVICE_PORT_NAME = "webserver-port" - -INGRESS_NAME = "webserver-ingress" - - -def define_namespace(name: str) -> k8s.V1Namespace: - return k8s.V1Namespace(metadata=k8s.V1ObjectMeta(name=name)) - - -def _define_webserver_deployment() -> k8s.V1Deployment: - # Define Pod container template - container = k8s.V1Container( - name="echo", - image="hashicorp/http-echo", - command=[ - "/http-echo", - f"-listen=:{CONTAINER_PORT}", - f'-text="Echo server on port {CONTAINER_PORT}"', - ], - ports=[k8s.V1ContainerPort(container_port=CONTAINER_PORT)], - resources=k8s.V1ResourceRequirements( - requests={"cpu": "100m", "memory": "200Mi"}, - limits={"cpu": "500m", "memory": "500Mi"}, - ), - ) - - # Create and configure a spec section - template = k8s.V1PodTemplateSpec( - metadata=k8s.V1ObjectMeta(labels={"app": APP_NAME}), - spec=k8s.V1PodSpec(containers=[container]), - ) - - # Create the specification of deployment - spec = k8s.V1DeploymentSpec( - replicas=1, - template=template, - selector=k8s.V1LabelSelector(match_labels={"app": APP_NAME}), - ) - - # Instantiate the deployment object - deployment = k8s.V1Deployment( - metadata=k8s.V1ObjectMeta(name=DEPLOYMENT_NAME), - spec=spec, - ) - - return deployment - - -def _define_webserver_service() -> k8s.V1Service: - spec = k8s.V1ServiceSpec( - selector={"app": APP_NAME}, - type="LoadBalancer", - ports=[ - k8s.V1ServicePort( - name=SERVICE_PORT_NAME, - port=SERVICE_PORT, - target_port=CONTAINER_PORT, - ) - ], - ) - - svc = k8s.V1Service( - metadata=k8s.V1ObjectMeta(name=SERVICE_NAME), - spec=spec, - ) - - return svc - - -def _define_webserver_ingress() -> k8s.V1Ingress: - spec = k8s.V1IngressSpec( - rules=[ - k8s.V1IngressRule( - http=k8s.V1HTTPIngressRuleValue( - paths=[ - k8s.V1HTTPIngressPath( - path="/", - path_type="Prefix", - backend=k8s.V1IngressBackend( - service=k8s.V1IngressServiceBackend( - name=SERVICE_NAME, - port=k8s.V1ServiceBackendPort( - name=SERVICE_PORT_NAME - ), - ) - ), - ) - ] - ) - ) - ] - ) - - ingress = k8s.V1Ingress( - metadata=k8s.V1ObjectMeta(name=INGRESS_NAME), - spec=spec, - ) - - return ingress - - -def define_resources() -> list[Any]: - return [ - _define_webserver_deployment(), - _define_webserver_service(), - _define_webserver_ingress(), - ] diff --git a/pyproject.toml b/pyproject.toml index 5206adf7128..199b3fbc321 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ dependencies = [ "jsonnet>=0.22.0", "jsonschema>=4.26.0", "jwcrypto>=1.6.0", - "kubernetes>=36.0.3", "locust>=2.46.4", "loguru>=0.7.3", "lxml>=6.1.3", @@ -34,7 +33,6 @@ dependencies = [ "matplotlib>=3.11.1", "numpy>=2.5.2", "oauthlib>=3.3.1", - "pem>=23.1.0", "pip-tools>=7.6.1", "psycopg[binary]>=3.3.5", "pvlib>=0.15.2", @@ -47,7 +45,6 @@ dependencies = [ "s2sphere>=0.2.5", "scipy>=1.18.1", "shapely>=2.1.2", - "structlog>=26.1.0", "termcolor>=3.3.0", "testcontainers>=4.15.0", "uas-standards>=4.4.0", @@ -119,7 +116,6 @@ package-dir = { "monitoring" = "monitoring" } where = ["."] include = [ "monitoring", - "monitoring.deployment_manager*", "monitoring.mock_uss*", "monitoring.monitorlib*", "monitoring.prober*", diff --git a/uv.lock b/uv.lock index 01e7aeac415..13bbc37db38 100644 --- a/uv.lock +++ b/uv.lock @@ -621,15 +621,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/75/23/529140fe1aab80fc6992f93a706deec709140a6397439139a054e1515c45/docker-7.2.0-py3-none-any.whl", hash = "sha256:a3f45fdeb9165e2d25d9a1d02ddf3bc70fb572cf5ebbf9b58558c22caf29b71f", size = 148775, upload-time = "2026-07-09T14:53:45.224Z" }, ] -[[package]] -name = "durationpy" -version = "0.10" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/9d/a4/e44218c2b394e31a6dd0d6b095c4e1f32d0be54c2a4b250032d717647bab/durationpy-0.10.tar.gz", hash = "sha256:1fa6893409a6e739c9c72334fc65cca1f355dbdd93405d30f726deb5bde42fba", size = 3335, upload-time = "2025-05-17T13:52:37.26Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/b0/0d/9feae160378a3553fa9a339b0e9c1a048e147a4127210e286ef18b730f03/durationpy-0.10-py3-none-any.whl", hash = "sha256:3b41e1b601234296b4fb368338fdcd3e13e0b4fb5b67345948f4f2bf9868b286", size = 3922, upload-time = "2025-05-17T13:52:36.463Z" }, -] - [[package]] name = "faker" version = "40.41.0" @@ -1083,7 +1074,6 @@ dependencies = [ { name = "jsonnet" }, { name = "jsonschema" }, { name = "jwcrypto" }, - { name = "kubernetes" }, { name = "locust" }, { name = "loguru" }, { name = "lxml" }, @@ -1091,7 +1081,6 @@ dependencies = [ { name = "matplotlib" }, { name = "numpy" }, { name = "oauthlib" }, - { name = "pem" }, { name = "pip-tools" }, { name = "psycopg", extra = ["binary"] }, { name = "pvlib" }, @@ -1104,7 +1093,6 @@ dependencies = [ { name = "s2sphere" }, { name = "scipy" }, { name = "shapely" }, - { name = "structlog" }, { name = "termcolor" }, { name = "testcontainers" }, { name = "uas-standards" }, @@ -1140,7 +1128,6 @@ requires-dist = [ { name = "jsonnet", specifier = ">=0.22.0" }, { name = "jsonschema", specifier = ">=4.26.0" }, { name = "jwcrypto", specifier = ">=1.6.0" }, - { name = "kubernetes", specifier = ">=36.0.3" }, { name = "locust", specifier = ">=2.46.4" }, { name = "loguru", specifier = ">=0.7.3" }, { name = "lxml", specifier = ">=6.1.3" }, @@ -1148,7 +1135,6 @@ requires-dist = [ { name = "matplotlib", specifier = ">=3.11.1" }, { name = "numpy", specifier = ">=2.5.2" }, { name = "oauthlib", specifier = ">=3.3.1" }, - { name = "pem", specifier = ">=23.1.0" }, { name = "pip-tools", specifier = ">=7.6.1" }, { name = "psycopg", extras = ["binary"], specifier = ">=3.3.5" }, { name = "pvlib", specifier = ">=0.15.2" }, @@ -1161,7 +1147,6 @@ requires-dist = [ { name = "s2sphere", specifier = ">=0.2.5" }, { name = "scipy", specifier = ">=1.18.1" }, { name = "shapely", specifier = ">=2.1.2" }, - { name = "structlog", specifier = ">=26.1.0" }, { name = "termcolor", specifier = ">=3.3.0" }, { name = "testcontainers", specifier = ">=4.15.0" }, { name = "uas-standards", specifier = ">=4.4.0" }, @@ -1321,27 +1306,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/99/a2/ca7dc962848040befed12732dff6acae7fb3c4f6fc4272b3f6c9a30b8713/kiwisolver-1.5.0-cp314-cp314t-win_arm64.whl", hash = "sha256:58f812017cd2985c21fbffb4864d59174d4903dd66fa23815e74bbc7a0e2dd57", size = 70032, upload-time = "2026-03-09T13:15:34.411Z" }, ] -[[package]] -name = "kubernetes" -version = "36.0.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "certifi" }, - { name = "durationpy" }, - { name = "python-dateutil" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "requests-oauthlib" }, - { name = "six" }, - { name = "urllib3" }, - { name = "websocket-client" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/ca/57/b07b96353f902aa1bdbe00e878e3a12a137977d03a962479785576aa8ec9/kubernetes-36.0.3.tar.gz", hash = "sha256:36993ed25ce59b789c9341473a228fcf268504a2fec7c2b2b1531d73072e5ce7", size = 2337528, upload-time = "2026-07-13T20:38:12.128Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/30/a96d47df739689ac0001ade0afefc16e3b477fc2fb426b568515fdc8afce/kubernetes-36.0.3-py2.py3-none-any.whl", hash = "sha256:8fde9241c4b298e6374a069dcf728359b4e72c2fb29489a975ba4e1c047cf10f", size = 4618066, upload-time = "2026-07-13T20:38:10.172Z" }, -] - [[package]] name = "locust" version = "2.46.7" @@ -1880,15 +1844,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/cf/6a51b2c38980e04c279fd2fa908a1b0982064e860444acfca4ec2e2c8359/pandas-3.0.5-cp314-cp314t-win_arm64.whl", hash = "sha256:3c5015fd1730fbf883647e88068176c839c102cea883ba1769a6f4593bfc1f8c", size = 9509776, upload-time = "2026-07-22T22:19:26.694Z" }, ] -[[package]] -name = "pem" -version = "23.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/05/86/16c0b6789816f8d53f2f208b5a090c9197da8a6dae4d490554bb1bedbb09/pem-23.1.0.tar.gz", hash = "sha256:06503ff2441a111f853ce4e8b9eb9d5fedb488ebdbf560115d3dd53a1b4afc73", size = 43796, upload-time = "2023-06-21T10:24:40.539Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c9/97/8299a481ae6c08494b5d53511e6a4746775d8a354c685c69d8796b2ed482/pem-23.1.0-py3-none-any.whl", hash = "sha256:78bbb1e75b737891350cb9499cbba31da5d59545f360f44163c0bc751cad55d3", size = 9195, upload-time = "2023-06-21T10:24:39.164Z" }, -] - [[package]] name = "pillow" version = "12.3.0" @@ -2531,19 +2486,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" }, ] -[[package]] -name = "requests-oauthlib" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "oauthlib" }, - { name = "requests" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/42/f2/05f29bc3913aea15eb670be136045bf5c5bbf4b99ecb839da9b422bb2c85/requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9", size = 55650, upload-time = "2024-03-22T20:32:29.939Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/3b/5d/63d4ae3b9daea098d5d6f5da83984853c1bbacd5dc826764b249fe119d24/requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36", size = 24179, upload-time = "2024-03-22T20:32:28.055Z" }, -] - [[package]] name = "rpds-py" version = "2026.6.3" @@ -2818,15 +2760,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/eb/dc/ad025c1ee131eba60c69f4dd5779b18fcf1e6b21a343e2162a84d5d133c7/soupsieve-2.9.2-py3-none-any.whl", hash = "sha256:8089a26fd974ca7a1f30276d3d8492ab266ab15af581642dfe8aa162e0c1c823", size = 37370, upload-time = "2026-08-07T00:57:23.524Z" }, ] -[[package]] -name = "structlog" -version = "26.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/5e/89/b4a0bcfdf4f71a3dea31379f095929613d7e4528a0996bca6aa964cd0dca/structlog-26.1.0.tar.gz", hash = "sha256:f63a716cbd1b1291cf7661de7794b455acfa4c43c5bcf1630e6ad5ddc1adb3b7", size = 1459881, upload-time = "2026-06-06T07:33:39.348Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/a9/18/489c97b834dfff9cf2fc2507cede4bcd4b11e67f84bc462acd1992496f86/structlog-26.1.0-py3-none-any.whl", hash = "sha256:e081a26d6c373e6d201eca24eede26d8ffab07f88f477822e679183428d3d91e", size = 73764, upload-time = "2026-06-06T07:33:38.046Z" }, -] - [[package]] name = "termcolor" version = "3.3.0"