From 278428f38a9f8444069f6ca220ed3e46d8332bd0 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 18:56:15 +0000 Subject: [PATCH 01/21] initial setup of python --- .devcontainer/devcontainer.json | 15 +-- .gitignore | 154 +++++---------------------- deno.json => old/deno.json | 0 deno.lock => old/deno.lock | 0 main.js => old/main.js | 0 {src => old/src}/gitops.js | 0 {src => old/src}/index.js | 0 {src => old/src}/logic/codefresh.js | 0 {src => old/src}/logic/core.js | 0 {src => old/src}/logic/k8s.js | 0 {src => old/src}/onprem.js | 0 {src => old/src}/oss.js | 0 {src => old/src}/pipelines.js | 0 pyproject.toml | 17 +++ src/cf_support/cli.py | 17 +++ src/cf_support/commands/gitops.py | 10 ++ src/cf_support/commands/onprem.py | 10 ++ src/cf_support/commands/oss.py | 10 ++ src/cf_support/commands/pipelines.py | 11 ++ 19 files changed, 109 insertions(+), 135 deletions(-) rename deno.json => old/deno.json (100%) rename deno.lock => old/deno.lock (100%) rename main.js => old/main.js (100%) rename {src => old/src}/gitops.js (100%) rename {src => old/src}/index.js (100%) rename {src => old/src}/logic/codefresh.js (100%) rename {src => old/src}/logic/core.js (100%) rename {src => old/src}/logic/k8s.js (100%) rename {src => old/src}/onprem.js (100%) rename {src => old/src}/oss.js (100%) rename {src => old/src}/pipelines.js (100%) create mode 100644 pyproject.toml create mode 100644 src/cf_support/cli.py create mode 100644 src/cf_support/commands/gitops.py create mode 100644 src/cf_support/commands/onprem.py create mode 100644 src/cf_support/commands/oss.py create mode 100644 src/cf_support/commands/pipelines.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 441e305..2edac87 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,17 +2,18 @@ { "name": "Codefresh Support Package", "image": "mcr.microsoft.com/devcontainers/universal:3", - "onCreateCommand": "curl -fsSL https://deno.land/install.sh | sh -s -- -y", + // "onCreateCommand": "curl -fsSL https://deno.land/install.sh | sh -s -- -y", "customizations": { "vscode": { - "settings": { - "deno.enable": true, - "deno.lint": true - }, + // "settings": { + // "deno.enable": true, + // "deno.lint": true + // }, "extensions": [ - "denoland.vscode-deno", "davidanson.vscode-markdownlint", - "redhat.vscode-yaml" + "redhat.vscode-yaml", + "usernamehw.errorlens", + "ms-python.python" ] } } diff --git a/.gitignore b/.gitignore index 13c73c3..e06640b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,132 +1,30 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.pyc +*.pyd +*.so + +# Virtual environment +venv/ +.venv/ +env/ + +# IDE specific files +.idea/ +*.iml +.vscode/ + +# Jupyter Notebook +.ipynb_checkpoints + +# Distribution / build files +dist/ +build/ +*.egg-info/ + # Logs -logs *.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* +# OS generated files .DS_Store -bin \ No newline at end of file +Thumbs.db \ No newline at end of file diff --git a/deno.json b/old/deno.json similarity index 100% rename from deno.json rename to old/deno.json diff --git a/deno.lock b/old/deno.lock similarity index 100% rename from deno.lock rename to old/deno.lock diff --git a/main.js b/old/main.js similarity index 100% rename from main.js rename to old/main.js diff --git a/src/gitops.js b/old/src/gitops.js similarity index 100% rename from src/gitops.js rename to old/src/gitops.js diff --git a/src/index.js b/old/src/index.js similarity index 100% rename from src/index.js rename to old/src/index.js diff --git a/src/logic/codefresh.js b/old/src/logic/codefresh.js similarity index 100% rename from src/logic/codefresh.js rename to old/src/logic/codefresh.js diff --git a/src/logic/core.js b/old/src/logic/core.js similarity index 100% rename from src/logic/core.js rename to old/src/logic/core.js diff --git a/src/logic/k8s.js b/old/src/logic/k8s.js similarity index 100% rename from src/logic/k8s.js rename to old/src/logic/k8s.js diff --git a/src/onprem.js b/old/src/onprem.js similarity index 100% rename from src/onprem.js rename to old/src/onprem.js diff --git a/src/oss.js b/old/src/oss.js similarity index 100% rename from src/oss.js rename to old/src/oss.js diff --git a/src/pipelines.js b/old/src/pipelines.js similarity index 100% rename from src/pipelines.js rename to old/src/pipelines.js diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a805f31 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,17 @@ +[project] +name = "cf-support" +dynamic = ["version"] +description = "Codefresh Support Package" +authors = [{ name = "Codefresh Support", email = "support@codefresh.io" }] +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "click>=8.1", +] + +[project.scripts] +cf-support = "cf_support.cli:cli" + +[build-system] +requires = ["setuptools >= 77.0.3"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/src/cf_support/cli.py b/src/cf_support/cli.py new file mode 100644 index 0000000..faed8f9 --- /dev/null +++ b/src/cf_support/cli.py @@ -0,0 +1,17 @@ +import click +from .commands import pipelines, gitops, onprem, oss + +@click.group() +def cli(): + """Codefresh Support Package + + Tool to gather information for Codefresh Support + """ + pass + +# Add individual commands directly to the main 'cli' group +cli.add_command(pipelines.pipelines_command) +cli.add_command(gitops.gitops_command) +cli.add_command(onprem.onprem_command) +cli.add_command(oss.oss_command) + diff --git a/src/cf_support/commands/gitops.py b/src/cf_support/commands/gitops.py new file mode 100644 index 0000000..bb6483f --- /dev/null +++ b/src/cf_support/commands/gitops.py @@ -0,0 +1,10 @@ +import click + +@click.command(name='gitops') # Use @click.command() directly +@click.option('-n','--namespace', help='The namespace where the GitOps Runtime is installed') +def gitops_command(namespace): + """Collect data for the Codefresh GitOps Runtime""" + + click.echo(f"Executing pipelines command with filter: {filter if filter else 'none'}") + # Add your core logic for the 'pipelines' command here. + # This might involve calling functions from 'utils.py' or directly performing actions. \ No newline at end of file diff --git a/src/cf_support/commands/onprem.py b/src/cf_support/commands/onprem.py new file mode 100644 index 0000000..c62e827 --- /dev/null +++ b/src/cf_support/commands/onprem.py @@ -0,0 +1,10 @@ +import click + +@click.command(name='onprem') # Use @click.command() directly +@click.option('-n','--namespace', help='The namespace where Codefresh OnPrem is installed') +def onprem_command(namespace): + """Collect data for the Codefresh OnPrem Installation""" + + click.echo(f"Executing pipelines command with filter: {filter if filter else 'none'}") + # Add your core logic for the 'pipelines' command here. + # This might involve calling functions from 'utils.py' or directly performing actions. \ No newline at end of file diff --git a/src/cf_support/commands/oss.py b/src/cf_support/commands/oss.py new file mode 100644 index 0000000..c12ef73 --- /dev/null +++ b/src/cf_support/commands/oss.py @@ -0,0 +1,10 @@ +import click + +@click.command(name='oss') # Use @click.command() directly +@click.option('-n','--namespace', help='The namespace where the OSS ArgoCD is installed') +def oss_command(namespace): + """Collect data for the Open Source ArgoCD""" + + click.echo(f"Executing pipelines command with filter: {filter if filter else 'none'}") + # Add your core logic for the 'pipelines' command here. + # This might involve calling functions from 'utils.py' or directly performing actions. \ No newline at end of file diff --git a/src/cf_support/commands/pipelines.py b/src/cf_support/commands/pipelines.py new file mode 100644 index 0000000..bf3d46e --- /dev/null +++ b/src/cf_support/commands/pipelines.py @@ -0,0 +1,11 @@ +import click + +@click.command(name='pipelines') # Use @click.command() directly +@click.option('-n','--namespace', help='The namespace where the Pipelines Runtime is installed') +@click.option('-r','--runtime', help='The name of the Pipelines Runtime') +def pipelines_command(namespace, runtime): + """Collect data for the Codefresh Pipelines Runtime""" + + click.echo(f"Executing pipelines command with filter: {filter if filter else 'none'}") + # Add your core logic for the 'pipelines' command here. + # This might involve calling functions from 'utils.py' or directly performing actions. \ No newline at end of file From ba5f2867aa6112d648c0893c8797d2e0d7ab5248 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:10:44 +0000 Subject: [PATCH 02/21] updated files --- pyproject.toml | 3 ++- src/cf_support/cli.py | 7 +++++++ src/cf_support/logic/codefresh.py | 0 src/cf_support/logic/core.py | 0 src/cf_support/logic/k8s.py | 0 5 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 src/cf_support/logic/codefresh.py create mode 100644 src/cf_support/logic/core.py create mode 100644 src/cf_support/logic/k8s.py diff --git a/pyproject.toml b/pyproject.toml index a805f31..db8bcca 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,11 +7,12 @@ readme = "README.md" requires-python = ">=3.11" dependencies = [ "click>=8.1", + "requests>=2.32" ] [project.scripts] cf-support = "cf_support.cli:cli" [build-system] -requires = ["setuptools >= 77.0.3"] +requires = ["setuptools>=80.9", "setuptools_scm>=8.3"] build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/src/cf_support/cli.py b/src/cf_support/cli.py index faed8f9..2271c5d 100644 --- a/src/cf_support/cli.py +++ b/src/cf_support/cli.py @@ -1,7 +1,14 @@ import click +import importlib.metadata from .commands import pipelines, gitops, onprem, oss +try: + __version__ = importlib.metadata.version("cf-support") +except importlib.metadata.PackageNotFoundError: + __version__ = "0.0.0+dev" + @click.group() +@click.version_option(version=__version__, prog_name="cf-support") def cli(): """Codefresh Support Package diff --git a/src/cf_support/logic/codefresh.py b/src/cf_support/logic/codefresh.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cf_support/logic/core.py b/src/cf_support/logic/core.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cf_support/logic/k8s.py b/src/cf_support/logic/k8s.py new file mode 100644 index 0000000..e69de29 From 2065b8854d5be4d9a762bb4744f7c5119a229e34 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:22:11 +0000 Subject: [PATCH 03/21] added version --- .gitignore | 5 ++++- pyproject.toml | 7 +++++-- src/cf_support/cli.py | 7 +++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index e06640b..baaeda2 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,7 @@ build/ # OS generated files .DS_Store -Thumbs.db \ No newline at end of file +Thumbs.db + +# Auto Generated Version +_version.py \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index db8bcca..9cbab43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ dynamic = ["version"] description = "Codefresh Support Package" authors = [{ name = "Codefresh Support", email = "support@codefresh.io" }] readme = "README.md" -requires-python = ">=3.11" +requires-python = ">=3.12" dependencies = [ "click>=8.1", "requests>=2.32" @@ -15,4 +15,7 @@ cf-support = "cf_support.cli:cli" [build-system] requires = ["setuptools>=80.9", "setuptools_scm>=8.3"] -build-backend = "setuptools.build_meta" \ No newline at end of file +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "src/cf_support/_version.py" \ No newline at end of file diff --git a/src/cf_support/cli.py b/src/cf_support/cli.py index 2271c5d..e6b4f08 100644 --- a/src/cf_support/cli.py +++ b/src/cf_support/cli.py @@ -1,11 +1,10 @@ import click -import importlib.metadata from .commands import pipelines, gitops, onprem, oss try: - __version__ = importlib.metadata.version("cf-support") -except importlib.metadata.PackageNotFoundError: - __version__ = "0.0.0+dev" + from ._version import version as __version__ +except ImportError: + __version__ = "0.0.0+dev.uninstalled" @click.group() @click.version_option(version=__version__, prog_name="cf-support") From bba034e5c164511fb1ca05b7d6ab2d0f70e6c5bb Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:34:18 +0000 Subject: [PATCH 04/21] updated codefresh --- .devcontainer/devcontainer.json | 2 +- pyproject.toml | 4 +- src/cf_support/logic/codefresh.py | 75 +++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2edac87..6f54b86 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ { "name": "Codefresh Support Package", "image": "mcr.microsoft.com/devcontainers/universal:3", - // "onCreateCommand": "curl -fsSL https://deno.land/install.sh | sh -s -- -y", + "onCreateCommand": "python3 -m venv .venv && source .venv/bin/activate && pip3 install -e .", "customizations": { "vscode": { // "settings": { diff --git a/pyproject.toml b/pyproject.toml index 9cbab43..1d25d87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,9 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "click>=8.1", - "requests>=2.32" + "kubernetes>=31.1" + "PyYAML>=6.0" + "requests>=2.32", ] [project.scripts] diff --git a/src/cf_support/logic/codefresh.py b/src/cf_support/logic/codefresh.py index e69de29..b6303ad 100644 --- a/src/cf_support/logic/codefresh.py +++ b/src/cf_support/logic/codefresh.py @@ -0,0 +1,75 @@ +import os +import yaml +import requests + +def get_codefresh_credentials(): + env_token = os.getenv("CF_API_KEY") + env_url = os.getenv("CF_URL") + + if env_token and env_url: + return { + "headers": {"Authorization": env_token}, + "base_url": f"{env_url}/api", + } + + config_path = ( + f"{os.getenv('USERPROFILE')}/.cfconfig" + if os.name == "nt" + else f"{os.getenv('HOME')}/.cfconfig" + ) + + with open(config_path, "r") as config_file: + config = yaml.safe_load(config_file) + + current_context = config["contexts"].get(config["current-context"]) + + if not current_context: + return None + + return { + "headers": {"Authorization": current_context["token"]}, + "base_url": f"{current_context['url']}/api", + } + +def get_account_runtimes(cf_creds): + response = requests.get( + f"{cf_creds['base_url']}/runtime-environments", + headers=cf_creds["headers"], + ) + return response.json() + +def get_runtime_spec(cf_creds, runtime): + response = requests.get( + f"{cf_creds['base_url']}/runtime-environments/{runtime}", + headers=cf_creds["headers"], + ) + return response.json() + +def get_all_accounts(cf_creds): + response = requests.get( + f"{cf_creds['base_url']}/admin/accounts", + headers=cf_creds["headers"], + ) + return response.json() + +def get_all_runtimes(cf_creds): + response = requests.get( + f"{cf_creds['base_url']}/admin/runtime-environments", + headers=cf_creds["headers"], + ) + return response.json() + +def get_total_users(cf_creds): + response = requests.get( + f"{cf_creds['base_url']}/admin/user?limit=1&page=1", + headers=cf_creds["headers"], + ) + users = response.json() + return {"totalUsers": users["total"]} + +def get_system_feature_flags(cf_creds): + response = requests.get( + f"{cf_creds['base_url']}/admin/features", + headers=cf_creds["headers"], + ) + return response.json() \ No newline at end of file From 2f18387933c65333f579cd3fb7a17d968d7de98c Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:43:13 +0000 Subject: [PATCH 05/21] updated dev container --- .devcontainer/devcontainer.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 6f54b86..e120803 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,12 +2,9 @@ { "name": "Codefresh Support Package", "image": "mcr.microsoft.com/devcontainers/universal:3", - "onCreateCommand": "python3 -m venv .venv && source .venv/bin/activate && pip3 install -e .", "customizations": { "vscode": { // "settings": { - // "deno.enable": true, - // "deno.lint": true // }, "extensions": [ "davidanson.vscode-markdownlint", From daaa0b0d3d167949355d486f41761960ccd60a42 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:48:59 +0000 Subject: [PATCH 06/21] update the build-system --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 1d25d87..87ea292 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ dependencies = [ cf-support = "cf_support.cli:cli" [build-system] -requires = ["setuptools>=80.9", "setuptools_scm>=8.3"] +requires = ["setuptools>=80.9", "setuptools_scm[toml]>=8.3"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] From 27a0efdc159d61f3b29fa3601d4211b8c39e1bbe Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:50:46 +0000 Subject: [PATCH 07/21] fixed missing comma --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 87ea292..91230f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,9 +7,9 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "click>=8.1", - "kubernetes>=31.1" - "PyYAML>=6.0" - "requests>=2.32", + "kubernetes>=31.1", # Added comma here for consistency with list + "PyYAML>=6.0", # Added comma here for consistency with list + "requests>=2.32" # Removed trailing comma, as it's the last item ] [project.scripts] From 8020a9f1bac5bb74e4e61984ebd45edf9b764ea7 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:51:32 +0000 Subject: [PATCH 08/21] removed TOML comments --- pyproject.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 91230f1..1158a6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,9 +7,9 @@ readme = "README.md" requires-python = ">=3.12" dependencies = [ "click>=8.1", - "kubernetes>=31.1", # Added comma here for consistency with list - "PyYAML>=6.0", # Added comma here for consistency with list - "requests>=2.32" # Removed trailing comma, as it's the last item + "kubernetes>=31.1", + "PyYAML>=6.0", + "requests>=2.32" ] [project.scripts] From baba50d26384ed3c659cb9bc2012c63a4625d31e Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:57:40 +0000 Subject: [PATCH 09/21] auto install dependicies for package --- .devcontainer/devcontainer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e120803..e037352 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,6 +2,7 @@ { "name": "Codefresh Support Package", "image": "mcr.microsoft.com/devcontainers/universal:3", + "postStartCommand": "pip install -e .", "customizations": { "vscode": { // "settings": { From ab5471788bc7bf852eef7f28cad6c031b40fc914 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Thu, 17 Jul 2025 21:58:45 +0000 Subject: [PATCH 10/21] update to post create --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index e037352..f968e4d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,7 +2,7 @@ { "name": "Codefresh Support Package", "image": "mcr.microsoft.com/devcontainers/universal:3", - "postStartCommand": "pip install -e .", + "postCreateCommand": "pip install -e .", "customizations": { "vscode": { // "settings": { From 988a4190df95e3bfdeb68d199d83d7e45d84bf3d Mon Sep 17 00:00:00 2001 From: Britton Riggs <104870596+briggs-octo@users.noreply.github.com> Date: Fri, 18 Jul 2025 09:46:35 -0700 Subject: [PATCH 11/21] Refactoring, formatting (#39) * Refactoring, formatting * Delete codefresh.py * Adding python-dotenv dependency * update settings --------- Co-authored-by: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> --- .devcontainer/devcontainer.json | 5 +- codresh-support-package.env | 3 + pyproject.toml | 3 +- src/cf_support/logic/codefresh.py | 75 ------------------- .../logic/controllers/account_controller.py | 14 ++++ .../logic/controllers/auth_controller.py | 48 ++++++++++++ .../logic/controllers/runtime_controller.py | 14 ++++ .../logic/controllers/system_controller.py | 37 +++++++++ .../logic/controllers/user_controller.py | 0 src/cf_support/logic/main.py | 14 ++++ src/cf_support/logic/models/STUB | 0 11 files changed, 135 insertions(+), 78 deletions(-) create mode 100644 codresh-support-package.env delete mode 100644 src/cf_support/logic/codefresh.py create mode 100644 src/cf_support/logic/controllers/account_controller.py create mode 100644 src/cf_support/logic/controllers/auth_controller.py create mode 100644 src/cf_support/logic/controllers/runtime_controller.py create mode 100644 src/cf_support/logic/controllers/system_controller.py create mode 100644 src/cf_support/logic/controllers/user_controller.py create mode 100644 src/cf_support/logic/main.py create mode 100644 src/cf_support/logic/models/STUB diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f968e4d..498753f 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,8 +5,9 @@ "postCreateCommand": "pip install -e .", "customizations": { "vscode": { - // "settings": { - // }, + "settings": { + "python.analysis.typeCheckingMode": "strict" + }, "extensions": [ "davidanson.vscode-markdownlint", "redhat.vscode-yaml", diff --git a/codresh-support-package.env b/codresh-support-package.env new file mode 100644 index 0000000..0309f0c --- /dev/null +++ b/codresh-support-package.env @@ -0,0 +1,3 @@ +CF_API_KEY= +CF_URL= +USERPROFILE= \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 1158a6c..c01dee0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,8 @@ dependencies = [ "click>=8.1", "kubernetes>=31.1", "PyYAML>=6.0", - "requests>=2.32" + "requests>=2.32", + "python-dotenv>=1.1.1" ] [project.scripts] diff --git a/src/cf_support/logic/codefresh.py b/src/cf_support/logic/codefresh.py deleted file mode 100644 index b6303ad..0000000 --- a/src/cf_support/logic/codefresh.py +++ /dev/null @@ -1,75 +0,0 @@ -import os -import yaml -import requests - -def get_codefresh_credentials(): - env_token = os.getenv("CF_API_KEY") - env_url = os.getenv("CF_URL") - - if env_token and env_url: - return { - "headers": {"Authorization": env_token}, - "base_url": f"{env_url}/api", - } - - config_path = ( - f"{os.getenv('USERPROFILE')}/.cfconfig" - if os.name == "nt" - else f"{os.getenv('HOME')}/.cfconfig" - ) - - with open(config_path, "r") as config_file: - config = yaml.safe_load(config_file) - - current_context = config["contexts"].get(config["current-context"]) - - if not current_context: - return None - - return { - "headers": {"Authorization": current_context["token"]}, - "base_url": f"{current_context['url']}/api", - } - -def get_account_runtimes(cf_creds): - response = requests.get( - f"{cf_creds['base_url']}/runtime-environments", - headers=cf_creds["headers"], - ) - return response.json() - -def get_runtime_spec(cf_creds, runtime): - response = requests.get( - f"{cf_creds['base_url']}/runtime-environments/{runtime}", - headers=cf_creds["headers"], - ) - return response.json() - -def get_all_accounts(cf_creds): - response = requests.get( - f"{cf_creds['base_url']}/admin/accounts", - headers=cf_creds["headers"], - ) - return response.json() - -def get_all_runtimes(cf_creds): - response = requests.get( - f"{cf_creds['base_url']}/admin/runtime-environments", - headers=cf_creds["headers"], - ) - return response.json() - -def get_total_users(cf_creds): - response = requests.get( - f"{cf_creds['base_url']}/admin/user?limit=1&page=1", - headers=cf_creds["headers"], - ) - users = response.json() - return {"totalUsers": users["total"]} - -def get_system_feature_flags(cf_creds): - response = requests.get( - f"{cf_creds['base_url']}/admin/features", - headers=cf_creds["headers"], - ) - return response.json() \ No newline at end of file diff --git a/src/cf_support/logic/controllers/account_controller.py b/src/cf_support/logic/controllers/account_controller.py new file mode 100644 index 0000000..a5a08a2 --- /dev/null +++ b/src/cf_support/logic/controllers/account_controller.py @@ -0,0 +1,14 @@ +import requests + + +class AccountController: + def __init__(self, cf_creds: dict[str, dict[str, str]]) -> None: + self.base_url = cf_creds["base_url"] + self.auth_headers = cf_creds["headers"] + + def get_runtimes(self): + response = requests.get( + f"{self.base_url}/runtime-environments", + headers=self.auth_headers["headers"], # type: ignore + ) + return response.json() diff --git a/src/cf_support/logic/controllers/auth_controller.py b/src/cf_support/logic/controllers/auth_controller.py new file mode 100644 index 0000000..1c65ca6 --- /dev/null +++ b/src/cf_support/logic/controllers/auth_controller.py @@ -0,0 +1,48 @@ +import yaml +import os + + +class AuthController: + def __init__(self, env_token: str | None, env_url: str | None) -> None: + self.env_token = env_token + self.env_url = env_url + + def get_cf_credentials( + self, + ) -> dict[str, dict[str, str] | str] | None: + env_token = self.env_token + env_url = self.env_url + cf_credentials: dict[str, dict[str, str] | str] | None = None + + if env_token and env_url: + auth_header: dict[str, str] = {"Authorization": env_token} + + cf_credentials = { + "headers": auth_header, + "base_url": f"{env_url}/api", + } + + else: + config_path = ( + f"{os.getenv('USERPROFILE')}/.cfconfig" + if os.name == "nt" + else f"{os.getenv('HOME')}/.cfconfig" + ) + + with open(config_path, "r") as config_file: + config = yaml.safe_load(config_file) + + current_context = config["contexts"].get(config["current-context"]) + + if current_context: + context_token = current_context["token"] + context_url = current_context["url"] + + if context_token and context_url: + auth_header = {"Authorization": context_token} + cf_credentials = { + "headers": auth_header, + "base_url": f"{context_url}/api", + } + + return cf_credentials diff --git a/src/cf_support/logic/controllers/runtime_controller.py b/src/cf_support/logic/controllers/runtime_controller.py new file mode 100644 index 0000000..55bf49b --- /dev/null +++ b/src/cf_support/logic/controllers/runtime_controller.py @@ -0,0 +1,14 @@ +import requests + + +class RuntimeController: + def __init__(self, cf_creds: dict[str, dict[str, str]]) -> None: + self.base_url = cf_creds["base_url"] + self.auth_headers = cf_creds["headers"] + + def get_spec(self, runtime_name: str): + response = requests.get( + f"{self.base_url}/runtime-environments/{runtime_name}", + headers=self.auth_headers, + ) + return response.json() diff --git a/src/cf_support/logic/controllers/system_controller.py b/src/cf_support/logic/controllers/system_controller.py new file mode 100644 index 0000000..28a259c --- /dev/null +++ b/src/cf_support/logic/controllers/system_controller.py @@ -0,0 +1,37 @@ +import requests + + +class SystemController: + def __init__(self, cf_creds: dict[str, dict[str, str]]) -> None: + self.base_url = cf_creds["base_url"] + self.auth_headers = cf_creds["headers"] + + def get_all_accounts(self): + response = requests.get( + f"{self.base_url}/admin/accounts", + headers=self.auth_headers, + ) + return response.json() + + def get_all_runtimes(self): + response = requests.get( + f"{self.base_url}/admin/runtime-environments", + headers=self.auth_headers, + ) + return response.json() + + def get_feature_flags(self): + response = requests.get( + f"{self.base_url}/admin/features", + headers=self.auth_headers, + ) + return response.json() + + def get_total_users(self): + response = requests.get( + f"{self.base_url}/admin/user?limit=1&page=1", + headers=self.auth_headers, + ) + users = response.json() + + return {"totalUsers": users["total"]} diff --git a/src/cf_support/logic/controllers/user_controller.py b/src/cf_support/logic/controllers/user_controller.py new file mode 100644 index 0000000..e69de29 diff --git a/src/cf_support/logic/main.py b/src/cf_support/logic/main.py new file mode 100644 index 0000000..58ca014 --- /dev/null +++ b/src/cf_support/logic/main.py @@ -0,0 +1,14 @@ +from dotenv import load_dotenv +import os + +from .controllers.auth_controller import AuthController + +load_dotenv() + +def main(): + env_token = os.getenv("CF_API_KEY") + env_url = os.getenv("CF_URL") + auth_controller = AuthController(env_token, env_url) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/src/cf_support/logic/models/STUB b/src/cf_support/logic/models/STUB new file mode 100644 index 0000000..e69de29 From 15361d23e20b6fd82bb81d029ed8a65f58911259 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Fri, 18 Jul 2025 09:55:11 -0700 Subject: [PATCH 12/21] remove dev conttainer --- .devcontainer/devcontainer.json | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 498753f..0000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,19 +0,0 @@ -// For format details, see https://aka.ms/devcontainer.json. -{ - "name": "Codefresh Support Package", - "image": "mcr.microsoft.com/devcontainers/universal:3", - "postCreateCommand": "pip install -e .", - "customizations": { - "vscode": { - "settings": { - "python.analysis.typeCheckingMode": "strict" - }, - "extensions": [ - "davidanson.vscode-markdownlint", - "redhat.vscode-yaml", - "usernamehw.errorlens", - "ms-python.python" - ] - } - } -} From a7227e77c0ab565c193b7bedd57f922135b61010 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Fri, 18 Jul 2025 09:56:53 -0700 Subject: [PATCH 13/21] updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index baaeda2..a2dc396 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ build/ # Logs *.log +# ignore env +.env + # OS generated files .DS_Store Thumbs.db From ca761542adbada727b09e96547d3fef2e349d614 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Fri, 18 Jul 2025 13:35:35 -0700 Subject: [PATCH 14/21] updated auth_controller for loading cfconfig --- src/cf_support/logic/controllers/auth_controller.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/cf_support/logic/controllers/auth_controller.py b/src/cf_support/logic/controllers/auth_controller.py index 1c65ca6..ddf0079 100644 --- a/src/cf_support/logic/controllers/auth_controller.py +++ b/src/cf_support/logic/controllers/auth_controller.py @@ -29,10 +29,13 @@ def get_cf_credentials( else f"{os.getenv('HOME')}/.cfconfig" ) - with open(config_path, "r") as config_file: - config = yaml.safe_load(config_file) + try: + with open(config_path, "r") as config_file: + config = yaml.safe_load(config_file) - current_context = config["contexts"].get(config["current-context"]) + current_context = config["contexts"].get(config["current-context"]) + except (FileNotFoundError, PermissionError, yaml.YAMLError): + current_context = None if current_context: context_token = current_context["token"] From 28adee8f23363e002fc5b263e1b89a88d098ddd0 Mon Sep 17 00:00:00 2001 From: Britton Riggs Date: Fri, 18 Jul 2025 19:47:33 -0700 Subject: [PATCH 15/21] Restructuring folders --- src/cf_support/cli.py | 23 ----------- .../controllers/account_controller.py | 0 .../controllers/auth_controller.py | 0 .../controllers/runtime_controller.py | 0 .../controllers/system_controller.py | 0 .../logic/controllers/user_controller.py | 0 src/cf_support/logic/k8s.py | 0 src/cf_support/logic/main.py | 14 ------- src/cf_support/main.py | 39 +++++++++++++++++++ src/cf_support/{logic => }/models/STUB | 0 src/cf_support/{logic => utilities}/core.py | 0 11 files changed, 39 insertions(+), 37 deletions(-) delete mode 100644 src/cf_support/cli.py rename src/cf_support/{logic => }/controllers/account_controller.py (100%) rename src/cf_support/{logic => }/controllers/auth_controller.py (100%) rename src/cf_support/{logic => }/controllers/runtime_controller.py (100%) rename src/cf_support/{logic => }/controllers/system_controller.py (100%) delete mode 100644 src/cf_support/logic/controllers/user_controller.py delete mode 100644 src/cf_support/logic/k8s.py delete mode 100644 src/cf_support/logic/main.py create mode 100644 src/cf_support/main.py rename src/cf_support/{logic => }/models/STUB (100%) rename src/cf_support/{logic => utilities}/core.py (100%) diff --git a/src/cf_support/cli.py b/src/cf_support/cli.py deleted file mode 100644 index e6b4f08..0000000 --- a/src/cf_support/cli.py +++ /dev/null @@ -1,23 +0,0 @@ -import click -from .commands import pipelines, gitops, onprem, oss - -try: - from ._version import version as __version__ -except ImportError: - __version__ = "0.0.0+dev.uninstalled" - -@click.group() -@click.version_option(version=__version__, prog_name="cf-support") -def cli(): - """Codefresh Support Package - - Tool to gather information for Codefresh Support - """ - pass - -# Add individual commands directly to the main 'cli' group -cli.add_command(pipelines.pipelines_command) -cli.add_command(gitops.gitops_command) -cli.add_command(onprem.onprem_command) -cli.add_command(oss.oss_command) - diff --git a/src/cf_support/logic/controllers/account_controller.py b/src/cf_support/controllers/account_controller.py similarity index 100% rename from src/cf_support/logic/controllers/account_controller.py rename to src/cf_support/controllers/account_controller.py diff --git a/src/cf_support/logic/controllers/auth_controller.py b/src/cf_support/controllers/auth_controller.py similarity index 100% rename from src/cf_support/logic/controllers/auth_controller.py rename to src/cf_support/controllers/auth_controller.py diff --git a/src/cf_support/logic/controllers/runtime_controller.py b/src/cf_support/controllers/runtime_controller.py similarity index 100% rename from src/cf_support/logic/controllers/runtime_controller.py rename to src/cf_support/controllers/runtime_controller.py diff --git a/src/cf_support/logic/controllers/system_controller.py b/src/cf_support/controllers/system_controller.py similarity index 100% rename from src/cf_support/logic/controllers/system_controller.py rename to src/cf_support/controllers/system_controller.py diff --git a/src/cf_support/logic/controllers/user_controller.py b/src/cf_support/logic/controllers/user_controller.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/cf_support/logic/k8s.py b/src/cf_support/logic/k8s.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/cf_support/logic/main.py b/src/cf_support/logic/main.py deleted file mode 100644 index 58ca014..0000000 --- a/src/cf_support/logic/main.py +++ /dev/null @@ -1,14 +0,0 @@ -from dotenv import load_dotenv -import os - -from .controllers.auth_controller import AuthController - -load_dotenv() - -def main(): - env_token = os.getenv("CF_API_KEY") - env_url = os.getenv("CF_URL") - auth_controller = AuthController(env_token, env_url) - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/src/cf_support/main.py b/src/cf_support/main.py new file mode 100644 index 0000000..ff40dc0 --- /dev/null +++ b/src/cf_support/main.py @@ -0,0 +1,39 @@ +from .commands import pipelines, gitops, onprem, oss +from .controllers.account_controller import AccountController +from .controllers.auth_controller import AuthController +from .controllers.runtime_controller import RuntimeController +from .controllers.system_controller import SystemController +from dotenv import load_dotenv +import click +import os + +load_dotenv() + + +def main(): + env_token = os.getenv("CF_API_KEY") + env_url = os.getenv("CF_URL") + auth_controller = AuthController(env_token, env_url) + + try: + from ._version import version as __version__ + except ImportError: + __version__ = "0.0.0+dev.uninstalled" + + @click.group() + @click.version_option(version=__version__, prog_name="cf-support") + def cli(): + """Codefresh Support Package + + Tool to gather information for Codefresh Support + """ + pass + + cli.add_command(pipelines.pipelines_command) + cli.add_command(gitops.gitops_command) + cli.add_command(onprem.onprem_command) + cli.add_command(oss.oss_command) + + +if __name__ == "__main__": + main() diff --git a/src/cf_support/logic/models/STUB b/src/cf_support/models/STUB similarity index 100% rename from src/cf_support/logic/models/STUB rename to src/cf_support/models/STUB diff --git a/src/cf_support/logic/core.py b/src/cf_support/utilities/core.py similarity index 100% rename from src/cf_support/logic/core.py rename to src/cf_support/utilities/core.py From 64575b49301586a7c08eeebfc25eeb1fff1b0e23 Mon Sep 17 00:00:00 2001 From: Britton Riggs Date: Mon, 21 Jul 2025 18:32:03 -0700 Subject: [PATCH 16/21] A little more folder restructure, adding logging --- pyproject.toml | 2 +- .../models/STUB => commands/__init__.py} | 0 src/{cf_support => }/commands/gitops.py | 0 src/{cf_support => }/commands/onprem.py | 0 src/{cf_support => }/commands/oss.py | 0 src/{cf_support => }/commands/pipelines.py | 0 .../core.py => controllers/__init__.py} | 0 .../controllers/account_controller.py | 5 ++++ .../controllers/auth_controller.py | 7 ++++- .../controllers/runtime_controller.py | 7 +++++ .../controllers/system_controller.py | 8 ++++++ src/{cf_support => }/main.py | 13 +++++---- src/models/STUB | 0 src/models/__init__.py | 0 src/utilities/__init__.py | 0 src/utilities/logger_config.py | 28 +++++++++++++++++++ src/utilities/utility.py | 0 17 files changed, 63 insertions(+), 7 deletions(-) rename src/{cf_support/models/STUB => commands/__init__.py} (100%) rename src/{cf_support => }/commands/gitops.py (100%) rename src/{cf_support => }/commands/onprem.py (100%) rename src/{cf_support => }/commands/oss.py (100%) rename src/{cf_support => }/commands/pipelines.py (100%) rename src/{cf_support/utilities/core.py => controllers/__init__.py} (100%) rename src/{cf_support => }/controllers/account_controller.py (65%) rename src/{cf_support => }/controllers/auth_controller.py (88%) rename src/{cf_support => }/controllers/runtime_controller.py (61%) rename src/{cf_support => }/controllers/system_controller.py (68%) rename src/{cf_support => }/main.py (65%) create mode 100644 src/models/STUB create mode 100644 src/models/__init__.py create mode 100644 src/utilities/__init__.py create mode 100644 src/utilities/logger_config.py create mode 100644 src/utilities/utility.py diff --git a/pyproject.toml b/pyproject.toml index c01dee0..670382e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,4 +21,4 @@ requires = ["setuptools>=80.9", "setuptools_scm[toml]>=8.3"] build-backend = "setuptools.build_meta" [tool.setuptools_scm] -write_to = "src/cf_support/_version.py" \ No newline at end of file +write_to = "src/_version.py" \ No newline at end of file diff --git a/src/cf_support/models/STUB b/src/commands/__init__.py similarity index 100% rename from src/cf_support/models/STUB rename to src/commands/__init__.py diff --git a/src/cf_support/commands/gitops.py b/src/commands/gitops.py similarity index 100% rename from src/cf_support/commands/gitops.py rename to src/commands/gitops.py diff --git a/src/cf_support/commands/onprem.py b/src/commands/onprem.py similarity index 100% rename from src/cf_support/commands/onprem.py rename to src/commands/onprem.py diff --git a/src/cf_support/commands/oss.py b/src/commands/oss.py similarity index 100% rename from src/cf_support/commands/oss.py rename to src/commands/oss.py diff --git a/src/cf_support/commands/pipelines.py b/src/commands/pipelines.py similarity index 100% rename from src/cf_support/commands/pipelines.py rename to src/commands/pipelines.py diff --git a/src/cf_support/utilities/core.py b/src/controllers/__init__.py similarity index 100% rename from src/cf_support/utilities/core.py rename to src/controllers/__init__.py diff --git a/src/cf_support/controllers/account_controller.py b/src/controllers/account_controller.py similarity index 65% rename from src/cf_support/controllers/account_controller.py rename to src/controllers/account_controller.py index a5a08a2..d01c64b 100644 --- a/src/cf_support/controllers/account_controller.py +++ b/src/controllers/account_controller.py @@ -1,12 +1,17 @@ +from utilities.logger_config import setup_logger import requests +logger = setup_logger(__name__) + class AccountController: def __init__(self, cf_creds: dict[str, dict[str, str]]) -> None: + logger.debug(f"{self.__class__.__name__} initialized") self.base_url = cf_creds["base_url"] self.auth_headers = cf_creds["headers"] def get_runtimes(self): + logger.info(f"{self.__class__.__name__} is getting account runtimes") response = requests.get( f"{self.base_url}/runtime-environments", headers=self.auth_headers["headers"], # type: ignore diff --git a/src/cf_support/controllers/auth_controller.py b/src/controllers/auth_controller.py similarity index 88% rename from src/cf_support/controllers/auth_controller.py rename to src/controllers/auth_controller.py index ddf0079..cd124db 100644 --- a/src/cf_support/controllers/auth_controller.py +++ b/src/controllers/auth_controller.py @@ -1,22 +1,27 @@ +from utilities.logger_config import setup_logger import yaml import os +logger = setup_logger(__name__) + class AuthController: def __init__(self, env_token: str | None, env_url: str | None) -> None: + logger.debug(f"{self.__class__.__name__} initialized") self.env_token = env_token self.env_url = env_url def get_cf_credentials( self, ) -> dict[str, dict[str, str] | str] | None: + logger.info(f"{self.__class__.__name__} is getting CF credentials") env_token = self.env_token env_url = self.env_url cf_credentials: dict[str, dict[str, str] | str] | None = None if env_token and env_url: auth_header: dict[str, str] = {"Authorization": env_token} - + cf_credentials = { "headers": auth_header, "base_url": f"{env_url}/api", diff --git a/src/cf_support/controllers/runtime_controller.py b/src/controllers/runtime_controller.py similarity index 61% rename from src/cf_support/controllers/runtime_controller.py rename to src/controllers/runtime_controller.py index 55bf49b..772388f 100644 --- a/src/cf_support/controllers/runtime_controller.py +++ b/src/controllers/runtime_controller.py @@ -1,12 +1,19 @@ +from utilities.logger_config import setup_logger import requests +logger = setup_logger(__name__) + class RuntimeController: def __init__(self, cf_creds: dict[str, dict[str, str]]) -> None: + logger.debug(f"{self.__class__.__name__} initialized") self.base_url = cf_creds["base_url"] self.auth_headers = cf_creds["headers"] def get_spec(self, runtime_name: str): + logger.info( + f"{self.__class__.__name__} is getting runtime spec for runtime '{runtime_name}'" + ) response = requests.get( f"{self.base_url}/runtime-environments/{runtime_name}", headers=self.auth_headers, diff --git a/src/cf_support/controllers/system_controller.py b/src/controllers/system_controller.py similarity index 68% rename from src/cf_support/controllers/system_controller.py rename to src/controllers/system_controller.py index 28a259c..36fb67e 100644 --- a/src/cf_support/controllers/system_controller.py +++ b/src/controllers/system_controller.py @@ -1,12 +1,17 @@ +from utilities.logger_config import setup_logger import requests +logger = setup_logger(__name__) + class SystemController: def __init__(self, cf_creds: dict[str, dict[str, str]]) -> None: + logger.debug(f"{self.__class__.__name__} initialized") self.base_url = cf_creds["base_url"] self.auth_headers = cf_creds["headers"] def get_all_accounts(self): + logger.info(f"{self.__class__.__name__} is getting all accounts") response = requests.get( f"{self.base_url}/admin/accounts", headers=self.auth_headers, @@ -14,6 +19,7 @@ def get_all_accounts(self): return response.json() def get_all_runtimes(self): + logger.info(f"{self.__class__.__name__} is getting all runtimes (cross-account)") response = requests.get( f"{self.base_url}/admin/runtime-environments", headers=self.auth_headers, @@ -21,6 +27,7 @@ def get_all_runtimes(self): return response.json() def get_feature_flags(self): + logger.info(f"{self.__class__.__name__} is getting feature flags") response = requests.get( f"{self.base_url}/admin/features", headers=self.auth_headers, @@ -28,6 +35,7 @@ def get_feature_flags(self): return response.json() def get_total_users(self): + logger.info(f"{self.__class__.__name__} is getting total users (cross-account)") response = requests.get( f"{self.base_url}/admin/user?limit=1&page=1", headers=self.auth_headers, diff --git a/src/cf_support/main.py b/src/main.py similarity index 65% rename from src/cf_support/main.py rename to src/main.py index ff40dc0..0e5b11e 100644 --- a/src/cf_support/main.py +++ b/src/main.py @@ -1,16 +1,19 @@ -from .commands import pipelines, gitops, onprem, oss -from .controllers.account_controller import AccountController -from .controllers.auth_controller import AuthController -from .controllers.runtime_controller import RuntimeController -from .controllers.system_controller import SystemController +from commands import pipelines, gitops, onprem, oss +from controllers.account_controller import AccountController +from controllers.auth_controller import AuthController +from controllers.runtime_controller import RuntimeController +from controllers.system_controller import SystemController from dotenv import load_dotenv +from utilities.logger_config import setup_logger import click import os load_dotenv() +logger = setup_logger(__name__) def main(): + logger.info("Starting CF Support CLI") env_token = os.getenv("CF_API_KEY") env_url = os.getenv("CF_URL") auth_controller = AuthController(env_token, env_url) diff --git a/src/models/STUB b/src/models/STUB new file mode 100644 index 0000000..e69de29 diff --git a/src/models/__init__.py b/src/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/utilities/__init__.py b/src/utilities/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/utilities/logger_config.py b/src/utilities/logger_config.py new file mode 100644 index 0000000..840091c --- /dev/null +++ b/src/utilities/logger_config.py @@ -0,0 +1,28 @@ +import logging +import os + +LOG_DIR = "logs" +LOG_FILE = "cli.log" +os.makedirs(LOG_DIR, exist_ok=True) + + +def setup_logger(name: str) -> logging.Logger: + logger = logging.getLogger(name) + logger.setLevel(logging.DEBUG) + + if not logger.hasHandlers(): + formatter = logging.Formatter( + "%(asctime)s - %(levelname)s - %(name)s.%(funcName)s() - %(message)s" + ) + + file_handler = logging.FileHandler(os.path.join(LOG_DIR, LOG_FILE)) + file_handler.setFormatter(formatter) + file_handler.setLevel(logging.DEBUG) + + stream_handler = logging.StreamHandler() + stream_handler.setFormatter(formatter) + stream_handler.setLevel(logging.INFO) + + logger.addHandler(file_handler) + + return logger diff --git a/src/utilities/utility.py b/src/utilities/utility.py new file mode 100644 index 0000000..e69de29 From 15060ba7c4255ac7e7b0dc1853f16751d6ded1c1 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:50:50 -0700 Subject: [PATCH 17/21] updated main.py and toml to get cli to work --- pyproject.toml | 2 +- src/main.py | 41 ++++++++++++++++++++--------------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 670382e..ee339e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ ] [project.scripts] -cf-support = "cf_support.cli:cli" +cf-support = "main:cli" [build-system] requires = ["setuptools>=80.9", "setuptools_scm[toml]>=8.3"] diff --git a/src/main.py b/src/main.py index 0e5b11e..f5f6300 100644 --- a/src/main.py +++ b/src/main.py @@ -12,31 +12,30 @@ logger = setup_logger(__name__) -def main(): - logger.info("Starting CF Support CLI") - env_token = os.getenv("CF_API_KEY") - env_url = os.getenv("CF_URL") - auth_controller = AuthController(env_token, env_url) +logger.info("Starting CF Support CLI") +env_token = os.getenv("CF_API_KEY") +env_url = os.getenv("CF_URL") +auth_controller = AuthController(env_token, env_url) - try: - from ._version import version as __version__ - except ImportError: - __version__ = "0.0.0+dev.uninstalled" +try: + from ._version import version as __version__ +except ImportError: + __version__ = "0.0.0+dev.uninstalled" - @click.group() - @click.version_option(version=__version__, prog_name="cf-support") - def cli(): - """Codefresh Support Package +@click.group() +@click.version_option(version=__version__, prog_name="cf-support") +def cli(): + """Codefresh Support Package - Tool to gather information for Codefresh Support - """ - pass + Tool to gather information for Codefresh Support + """ + pass - cli.add_command(pipelines.pipelines_command) - cli.add_command(gitops.gitops_command) - cli.add_command(onprem.onprem_command) - cli.add_command(oss.oss_command) +cli.add_command(pipelines.pipelines_command) +cli.add_command(gitops.gitops_command) +cli.add_command(onprem.onprem_command) +cli.add_command(oss.oss_command) if __name__ == "__main__": - main() + cli() From 703f4a911a928c70bd2f54539d594ea29a3f2ba9 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Tue, 22 Jul 2025 11:52:30 -0700 Subject: [PATCH 18/21] update version import --- src/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.py b/src/main.py index f5f6300..e52869d 100644 --- a/src/main.py +++ b/src/main.py @@ -18,7 +18,7 @@ auth_controller = AuthController(env_token, env_url) try: - from ._version import version as __version__ + from _version import version as __version__ except ImportError: __version__ = "0.0.0+dev.uninstalled" From f13c2e65599b851be7d0c47b59c00275a29201ec Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Wed, 23 Jul 2025 12:34:24 -0700 Subject: [PATCH 19/21] initial k8s controller --- src/controllers/k8s_controller.py | 155 ++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/controllers/k8s_controller.py diff --git a/src/controllers/k8s_controller.py b/src/controllers/k8s_controller.py new file mode 100644 index 0000000..00dc770 --- /dev/null +++ b/src/controllers/k8s_controller.py @@ -0,0 +1,155 @@ +from datetime import datetime +from typing import Dict, List, Optional, Any +from kubernetes import client, config +from kubernetes.client.rest import ApiException + + +class K8sController: + def __init__(self): + """Initialize Kubernetes client with auto-detected configuration.""" + + try: + # Try to load in-cluster config first, then kubeconfig + config.load_incluster_config() + except config.ConfigException: + config.load_kube_config() + + self.core_api = client.CoreV1Api() + self.apps_api = client.AppsV1Api() + self.batch_api = client.BatchV1Api() + self.storage_api = client.StorageV1Api() + self.crd_api = client.ApiextensionsV1Api() + self.custom_objects_api = client.CustomObjectsApi() + + def select_namespace(self) -> str: + """Interactive namespace selection.""" + namespaces = [ns.metadata.name for ns in self.core_api.list_namespace().items] + + for index, namespace in enumerate(namespaces, 1): + print(f"{index}. {namespace}") + + while True: + try: + selection = int(input('\nWhich Namespace are we using? (Number): ')) + if 1 <= selection <= len(namespaces): + return namespaces[selection - 1] + else: + print('Invalid selection. Please enter a number corresponding to one of the listed namespaces.') + except ValueError: + print('Invalid selection. Please enter a number corresponding to one of the listed namespaces.') + + def get_pod_logs(self, pod: Dict[str, Any]) -> Dict[str, str]: + """Get logs for all containers in a pod.""" + pod_name = pod['metadata']['name'] + namespace = pod['metadata']['namespace'] + containers = [container['name'] for container in pod['spec']['containers']] + + logs = {} + for container in containers: + try: + logs[container] = self.core_api.read_namespaced_pod_log( + name=pod_name, + namespace=namespace, + container=container, + timestamps=True + ) + except ApiException as error: + logs[container] = str(error) + + return logs + + def _get_crd(self, crd_type: str, namespace: str) -> Optional[Dict[str, Any]]: + """Get Custom Resource Definition objects.""" + try: + crd = self.crd_api.read_custom_resource_definition(crd_type) + + # Find served version + served_version = next( + (v.name for v in crd.spec.versions if v.served), + None + ) + + if not served_version: + return None + + # Get custom resources + response = self.custom_objects_api.list_namespaced_custom_object( + group=crd.spec.group, + version=served_version, + namespace=namespace, + plural=crd.spec.names.plural + ) + + return response + except ApiException: + return None + + def _get_sorted_events(self, namespace: str) -> client.V1EventList: + """Get events sorted by creation timestamp.""" + events = self.core_api.list_namespaced_event(namespace) + + # Sort events by creation timestamp + events.items.sort( + key=lambda event: event.metadata.creation_timestamp + ) + + return events + + def get_resources(self, namespace: str) -> Dict[str, callable]: + """Get dictionary of Kubernetes resource fetching functions.""" + k8s_resource_types = { + 'configmaps': lambda: self.core_api.list_namespaced_config_map(namespace), + 'cronjobs.batch': lambda: self.batch_api.list_namespaced_cron_job(namespace), + 'daemonsets.apps': lambda: self.apps_api.list_namespaced_daemon_set(namespace), + 'deployments.apps': lambda: self.apps_api.list_namespaced_deployment(namespace), + 'events.k8s.io': lambda: self._get_sorted_events(namespace), + 'jobs.batch': lambda: self.batch_api.list_namespaced_job(namespace), + 'nodes': lambda: self.core_api.list_node(), + 'pods': lambda: self.core_api.list_namespaced_pod(namespace), + 'serviceaccounts': lambda: self.core_api.list_namespaced_service_account(namespace), + 'services': lambda: self.core_api.list_namespaced_service(namespace), + 'statefulsets.apps': lambda: self.apps_api.list_namespaced_stateful_set(namespace), + 'persistentvolumeclaims': lambda: self.core_api.list_namespaced_persistent_volume_claim( + namespace, label_selector='io.codefresh.accountName' + ), + 'persistentvolumes': lambda: self.core_api.list_persistent_volume( + label_selector='io.codefresh.accountName' + ), + 'storageclasses.storage.k8s.io': lambda: self.storage_api.list_storage_class(), + + # Codefresh CRDs + 'products.codefresh.io': lambda: self._get_crd('products.codefresh.io', namespace), + 'promotionflows.codefresh.io': lambda: self._get_crd('promotionflows.codefresh.io', namespace), + 'promotionpolicies.codefresh.io': lambda: self._get_crd('promotionpolicies.codefresh.io', namespace), + 'promotiontemplates.codefresh.io': lambda: self._get_crd('promotiontemplates.codefresh.io', namespace), + 'restrictedgitsources.codefresh.io': lambda: self._get_crd('restrictedgitsources.codefresh.io', namespace), + + # ArgoProj CRDs + 'analysisruns.argoproj.io': lambda: self._get_crd('analysisruns.argoproj.io', namespace), + 'analysistemplates.argoproj.io': lambda: self._get_crd('analysistemplates.argoproj.io', namespace), + 'applications.argoproj.io': lambda: self._get_crd('applications.argoproj.io', namespace), + 'applicationsets.argoproj.io': lambda: self._get_crd('applicationsets.argoproj.io', namespace), + 'appprojects.argoproj.io': lambda: self._get_crd('appprojects.argoproj.io', namespace), + 'eventbus.argoproj.io': lambda: self._get_crd('eventbus.argoproj.io', namespace), + 'eventsources.argoproj.io': lambda: self._get_crd('eventsources.argoproj.io', namespace), + 'experiments.argoproj.io': lambda: self._get_crd('experiments.argoproj.io', namespace), + 'rollouts.argoproj.io': lambda: self._get_crd('rollouts.argoproj.io', namespace), + 'sensors.argoproj.io': lambda: self._get_crd('sensors.argoproj.io', namespace), + } + + return k8s_resource_types + + def fetch_all_resources(self, namespace: str) -> Dict[str, Any]: + """Fetch all Kubernetes resources for a namespace.""" + resources = {} + resource_functions = self.get_resources(namespace) + + for resource_type, fetch_func in resource_functions.items(): + try: + print(f"Fetching {resource_type}...") + resources[resource_type] = fetch_func() + except ApiException as e: + print(f"Failed to fetch {resource_type}: {e}") + resources[resource_type] = None + + return resources \ No newline at end of file From d20fabcaa637e5342695b8b2e95827ef968f1458 Mon Sep 17 00:00:00 2001 From: Luke Goodfellow <107487942+ThatAmatoGuy@users.noreply.github.com> Date: Fri, 25 Jul 2025 10:38:28 -0700 Subject: [PATCH 20/21] updating types --- src/controllers/k8s_controller.py | 64 +++++++++++++------------------ 1 file changed, 26 insertions(+), 38 deletions(-) diff --git a/src/controllers/k8s_controller.py b/src/controllers/k8s_controller.py index 00dc770..80d4ab1 100644 --- a/src/controllers/k8s_controller.py +++ b/src/controllers/k8s_controller.py @@ -1,7 +1,6 @@ -from datetime import datetime -from typing import Dict, List, Optional, Any -from kubernetes import client, config -from kubernetes.client.rest import ApiException +from typing import Dict, Optional, Any +from kubernetes import client, config # type: ignore +from kubernetes.client.rest import ApiException # type: ignore class K8sController: @@ -10,9 +9,9 @@ def __init__(self): try: # Try to load in-cluster config first, then kubeconfig - config.load_incluster_config() + config.load_incluster_config() # type: ignore except config.ConfigException: - config.load_kube_config() + config.load_kube_config() # type: ignore self.core_api = client.CoreV1Api() self.apps_api = client.AppsV1Api() @@ -23,7 +22,7 @@ def __init__(self): def select_namespace(self) -> str: """Interactive namespace selection.""" - namespaces = [ns.metadata.name for ns in self.core_api.list_namespace().items] + namespaces: list[str] = [ns.metadata.name for ns in self.core_api.list_namespace().items] # type: ignore for index, namespace in enumerate(namespaces, 1): print(f"{index}. {namespace}") @@ -44,15 +43,10 @@ def get_pod_logs(self, pod: Dict[str, Any]) -> Dict[str, str]: namespace = pod['metadata']['namespace'] containers = [container['name'] for container in pod['spec']['containers']] - logs = {} + logs: Dict[str, str] = {} for container in containers: try: - logs[container] = self.core_api.read_namespaced_pod_log( - name=pod_name, - namespace=namespace, - container=container, - timestamps=True - ) + logs[container] = self.core_api.read_namespaced_pod_log(name=pod_name, namespace=namespace, container=container, timestamps=True ) # type: ignore except ApiException as error: logs[container] = str(error) @@ -95,27 +89,28 @@ def _get_sorted_events(self, namespace: str) -> client.V1EventList: return events - def get_resources(self, namespace: str) -> Dict[str, callable]: - """Get dictionary of Kubernetes resource fetching functions.""" + def fetch_all_resources(self, namespace: str) -> Dict[str, Any]: + """Fetch all Kubernetes resources for a namespace.""" + k8s_resource_types = { - 'configmaps': lambda: self.core_api.list_namespaced_config_map(namespace), - 'cronjobs.batch': lambda: self.batch_api.list_namespaced_cron_job(namespace), - 'daemonsets.apps': lambda: self.apps_api.list_namespaced_daemon_set(namespace), - 'deployments.apps': lambda: self.apps_api.list_namespaced_deployment(namespace), - 'events.k8s.io': lambda: self._get_sorted_events(namespace), - 'jobs.batch': lambda: self.batch_api.list_namespaced_job(namespace), - 'nodes': lambda: self.core_api.list_node(), - 'pods': lambda: self.core_api.list_namespaced_pod(namespace), - 'serviceaccounts': lambda: self.core_api.list_namespaced_service_account(namespace), - 'services': lambda: self.core_api.list_namespaced_service(namespace), - 'statefulsets.apps': lambda: self.apps_api.list_namespaced_stateful_set(namespace), - 'persistentvolumeclaims': lambda: self.core_api.list_namespaced_persistent_volume_claim( + 'configmaps': lambda: self.core_api.list_namespaced_config_map(namespace), # type: ignore + 'cronjobs.batch': lambda: self.batch_api.list_namespaced_cron_job(namespace), # type: ignore + 'daemonsets.apps': lambda: self.apps_api.list_namespaced_daemon_set(namespace), # type: ignore + 'deployments.apps': lambda: self.apps_api.list_namespaced_deployment(namespace), # type: ignore + 'events.k8s.io': lambda: self._get_sorted_events(namespace), # type: ignore + 'jobs.batch': lambda: self.batch_api.list_namespaced_job(namespace), # type: ignore + 'nodes': lambda: self.core_api.list_node(), # type: ignore + 'pods': lambda: self.core_api.list_namespaced_pod(namespace), # type: ignore + 'serviceaccounts': lambda: self.core_api.list_namespaced_service_account(namespace), # type: ignore + 'services': lambda: self.core_api.list_namespaced_service(namespace), # type: ignore + 'statefulsets.apps': lambda: self.apps_api.list_namespaced_stateful_set(namespace), # type: ignore + 'persistentvolumeclaims': lambda: self.core_api.list_namespaced_persistent_volume_claim( # type: ignore namespace, label_selector='io.codefresh.accountName' ), - 'persistentvolumes': lambda: self.core_api.list_persistent_volume( + 'persistentvolumes': lambda: self.core_api.list_persistent_volume( # type: ignore label_selector='io.codefresh.accountName' ), - 'storageclasses.storage.k8s.io': lambda: self.storage_api.list_storage_class(), + 'storageclasses.storage.k8s.io': lambda: self.storage_api.list_storage_class(), # type: ignore # Codefresh CRDs 'products.codefresh.io': lambda: self._get_crd('products.codefresh.io', namespace), @@ -136,20 +131,13 @@ def get_resources(self, namespace: str) -> Dict[str, callable]: 'rollouts.argoproj.io': lambda: self._get_crd('rollouts.argoproj.io', namespace), 'sensors.argoproj.io': lambda: self._get_crd('sensors.argoproj.io', namespace), } - - return k8s_resource_types - - def fetch_all_resources(self, namespace: str) -> Dict[str, Any]: - """Fetch all Kubernetes resources for a namespace.""" resources = {} - resource_functions = self.get_resources(namespace) - for resource_type, fetch_func in resource_functions.items(): + for resource_type, fetch_func in k8s_resource_types.items(): try: print(f"Fetching {resource_type}...") resources[resource_type] = fetch_func() except ApiException as e: - print(f"Failed to fetch {resource_type}: {e}") resources[resource_type] = None return resources \ No newline at end of file From 5f9f9152417de58729964e7de8f1744835ad3e5a Mon Sep 17 00:00:00 2001 From: Britton Riggs Date: Tue, 29 Jul 2025 07:57:33 -0700 Subject: [PATCH 21/21] Adding initial utility class info --- src/utilities/utility.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/utilities/utility.py b/src/utilities/utility.py index e69de29..efc307c 100644 --- a/src/utilities/utility.py +++ b/src/utilities/utility.py @@ -0,0 +1,31 @@ +from utilities.logger_config import setup_logger +import tarfile +import yaml +import os + +logger = setup_logger(__name__) + + +class Utility: + def __init__(self) -> None: + pass + + def write_yaml_to_file(self, py_obj: object, filename: str): + with open(f"{filename}.yaml", "w") as f: + yaml.dump(py_obj, f, sort_keys=False) + print("Written to file successfully") + + def create_tar_gz(self, output_filename: str, source_dir: str): + if not os.path.isdir(source_dir): + raise NotADirectoryError(f"{source_dir} is not a valid directory") + + with tarfile.open(output_filename, "w:gz") as tar: + tar.add(source_dir, arcname=os.path.basename(source_dir)) + + def prepare_package(self, dir_path: str): + compressed_support_package = f"{dir_path}.tar.gz" + + logger.info(f"{self.__class__.__name__} Preparing the support package...") + + def process_dat(self): + pass \ No newline at end of file