diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/readme-static-files/goland-generate-rest-bindings.png b/.github/readme-static-files/goland-generate-rest-bindings.png new file mode 100644 index 00000000..77b9a346 Binary files /dev/null and b/.github/readme-static-files/goland-generate-rest-bindings.png differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..95e77d9f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,59 @@ +# Our desired pipeline using only a Nix shell environment +name: Check and build Kardinal + +on: + push: + branches: + - main + tags: + - "v*.*.*" + pull_request: + branches: + - main + +jobs: + check_nix: + name: Basic Check + runs-on: ubuntu-22.04 + steps: + - name: git checkout + uses: actions/checkout@v3 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Magic cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Check Nixpkgs inputs + uses: DeterminateSystems/flake-checker-action@main + with: + fail-mode: true + + # Nix-specific logic begins here + - name: Nix Check + run: | + nix flake check + + - name: Check Go formatting + run: | + nix develop --command test -z $(gofmt -l .) + + build_and_test_nix: + name: Test and build + needs: check_nix + runs-on: ubuntu-22.04 + steps: + - name: git checkout + uses: actions/checkout@v3 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Magic cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: Build Kardinal Manager images + run: | + nix build ./#containers.x86_64-linux.kardinal-manager.arm64 --no-link --print-out-paths + nix build ./#containers.x86_64-linux.kardinal-manager.amd64 --no-link --print-out-paths diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..63d70b9c --- /dev/null +++ b/.gitignore @@ -0,0 +1,88 @@ +.direnv +### VisualStudioCode template +.vscode/* + + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea +.idea/* + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + + + +### Go template +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + + +# local k8s files +.minikube/ + +# Python +.venv + +# local build folders +istio-1.22.0/ +kontrol-service/Run_kontrol_service diff --git a/README.md b/README.md new file mode 100644 index 00000000..54c8e88c --- /dev/null +++ b/README.md @@ -0,0 +1,87 @@ +# Kardinal + +## Developing instructions + +1. Enter the dev shell and start the local cluster: + +```bash +nix develop +``` + +2. You're also likely to use a local k8s, in this case minikube is available to use: + +```bash +kubectl config set-context minikube +minikube start --driver=docker --cpus=10 --memory 8192 --disk-size 32g +minikube addons enable ingress +minikube addons enable metrics-server +istioctl install --set profile=demo -y +minikube dashboard +``` + +On a second terminal, start the tunnel: + +```bash +minikube tunnel +``` + +## Deploying Kardinal Manager to local cluster + +You can use tilt deploy and keeping the image hot-reloading: + +```bash +tilt up +``` + +Or manually build it: + +```bash +# First set the docker context to minikube +eval $(minikube docker-env) +docker load < $(nix build ./#kardinal-manager-container --no-link --print-out-paths) +kubectl apply -f kardinal-manager/deployment +``` + +## Deploying Redis Overlay Service to local cluster + +Building and loading image into minikube: + +```bash +# First set the docker context to minikube +eval $(minikube docker-env) +docker load < $(nix build ./#redis-proxy-overlay-container --no-link --print-out-paths) +``` + +To build and run the service directly: + +```bash +nix run ./#redis-proxy-overlay +``` + +## Publishing multi-arch images + +To publish multi-arch images, you can use the following command: + +```bash +$(nix build .#publish--container --no-link --print-out-paths)/bin/push + +# For instance, to publish the redis proxy overlay image: +$(nix build .#publish-redis-proxy-overlay-container --no-link --print-out-paths)/bin/push +``` + +## Running Kardinal CLI + +To build and run the service directly: + +```bash +nix run ./#kardinal-cli +``` + +### Regenerate gomod2nix.toml + +You will need to do this every time a `go.mod` file is edited + +```bash +nix develop +gomod2nix generate +``` diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 00000000..024f0598 --- /dev/null +++ b/Tiltfile @@ -0,0 +1,39 @@ + +# Custom build tool for nix flakes +def build_flake_image(ref, path = "", output = "", resultfile = "result", deps = []): + build_cmd = "nix build {path}#{output} --refresh --no-link --print-out-paths".format( + path = path, + output = output + ) + commands = [ + "RESULT_IMAGE=$({cmd})".format(cmd = build_cmd), + "docker image load -i ${RESULT_IMAGE}", + 'IMG_NAME="$(tar -Oxf $RESULT_IMAGE manifest.json | jq -r ".[0].RepoTags[0]")"'.format(ref = ref), + "docker tag ${IMG_NAME} ${EXPECTED_REF}" + ] + custom_build( + ref, + command = [ + "nix-shell", + "--packages", + "coreutils", + "gnutar", + "jq", + "--run", + ";\n".join(commands), + ], + deps = deps, + ) + + +image_name = "kurtosistech/kardinal-manager" + +build_flake_image(image_name , ".", "kardinal-manager-container", deps=["./kardinal-manager"]) + +yaml_dir = "./kardinal-manager/deployment" +k8s_yaml(yaml=(yaml_dir + "/k8s.yaml")) + +if k8s_context: + k8s_context(k8s_context) + + diff --git a/examples/voting-app/docker-compose.yaml b/examples/voting-app/docker-compose.yaml new file mode 100644 index 00000000..611aa52d --- /dev/null +++ b/examples/voting-app/docker-compose.yaml @@ -0,0 +1,20 @@ +version: "3" +services: + azure-vote-back: + image: bitnami/redis:6.0.8 + container_name: redis-prod + environment: + ALLOW_EMPTY_PASSWORD: "yes" + REDIS_PORT_NUMBER: "6379" + ports: + - "6379:6379" + + azure-vote-front: + image: voting-app-ui + container_name: voting-app-ui + environment: + REDIS: redis-prod + ports: + - "80:80" + depends_on: + - azure-vote-back diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..af572cd7 --- /dev/null +++ b/flake.lock @@ -0,0 +1,102 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gomod2nix": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1717050755, + "narHash": "sha256-C9IEHABulv2zEDFA+Bf0E1nmfN4y6MIUe5eM2RCrDC0=", + "owner": "nix-community", + "repo": "gomod2nix", + "rev": "31b6d2e40b36456e792cd6cf50d5a8ddd2fa59a1", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "gomod2nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1718437845, + "narHash": "sha256-ZT7Oc1g4I4pHVGGjQFnewFVDRLH5cIZhEzODLz9YXeY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "752c634c09ceb50c45e751f8791cb45cb3d46c9e", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "gomod2nix": "gomod2nix", + "nixpkgs": "nixpkgs", + "unstable": "unstable" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "unstable": { + "locked": { + "lastModified": 1718530797, + "narHash": "sha256-pup6cYwtgvzDpvpSCFh1TEUjw2zkNpk8iolbKnyFmmU=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "b60ebf54c15553b393d144357375ea956f89e9a9", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..7b287257 --- /dev/null +++ b/flake.nix @@ -0,0 +1,187 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05"; + flake-utils.url = "github:numtide/flake-utils"; + unstable.url = "github:NixOS/nixpkgs/nixos-unstable"; + gomod2nix.url = "github:nix-community/gomod2nix"; + gomod2nix.inputs.nixpkgs.follows = "nixpkgs"; + gomod2nix.inputs.flake-utils.follows = "flake-utils"; + }; + outputs = { + self, + nixpkgs, + flake-utils, + unstable, + gomod2nix, + ... + }: + flake-utils.lib.eachDefaultSystem + ( + system: let + pkgs = import nixpkgs { + inherit system; + overlays = [ + (import "${gomod2nix}/overlay.nix") + ]; + }; + + service_names = ["kardinal-manager" "redis-proxy-overlay"]; + architectures = ["amd64" "arm64"]; + imageRegistry = "kurtosistech"; + + matchingContainerArch = + if builtins.match "aarch64-.*" system != null + then "arm64" + else if builtins.match "x86_64-.*" system != null + then "amd64" + else throw "Unsupported system type: ${system}"; + + mergeContainerPackages = acc: service: + pkgs.lib.recursiveUpdate acc { + packages."${service}-container" = self.containers.${system}.${service}.${matchingContainerArch}; + }; + + multiPlatformDockerPusher = acc: service: + pkgs.lib.recursiveUpdate acc { + packages."publish-${service}-container" = let + name = "${imageRegistry}/${service}"; + tagBase = "latest"; + images = + map ( + arch: rec { + inherit arch; + image = self.containers.${system}.${service}.${arch}; + tag = "${tagBase}-${arch}"; + } + ) + architectures; + loadAndPush = builtins.concatStringsSep "\n" (pkgs.lib.concatMap + ({ + arch, + image, + tag, + }: [ + "$docker load -i ${image}" + "$docker push ${name}:${tag}" + ]) + images); + imageNames = + builtins.concatStringsSep " " + (map ({ + arch, + image, + tag, + }: "${name}:${tag}") + images); + in + pkgs.writeTextFile { + inherit name; + text = '' + #!${pkgs.stdenv.shell} + set -euxo pipefail + docker=${pkgs.docker}/bin/docker + ${loadAndPush} + $docker manifest create --amend ${name}:${tagBase} ${imageNames} + $docker manifest push ${name}:${tagBase} + ''; + executable = true; + destination = "/bin/push"; + }; + }; + + systemOutput = rec { + devShells.default = pkgs.callPackage ./shell.nix { + inherit pkgs; + }; + + packages.kardinal-cli = pkgs.callPackage ./kardinal-cli/default.nix { + inherit pkgs; + }; + + packages.kardinal-manager = pkgs.callPackage ./kardinal-manager/default.nix { + inherit pkgs; + }; + + packages.redis-proxy-overlay = pkgs.callPackage ./sidecars/redis-overlay-service/default.nix { + inherit pkgs; + }; + + containers = let + os = "linux"; + all = + pkgs.lib.mapCartesianProduct ({ + arch, + service_name, + }: { + "${service_name}" = { + "${toString arch}" = let + nix_arch = + builtins.replaceStrings + ["arm64" "amd64"] ["aarch64" "x86_64"] + arch; + + container_pkgs = import nixpkgs { + system = "${nix_arch}-${os}"; + }; + + # if running from linux no cross-compilation is needed to palce the service in a container + needsCrossCompilation = + "${nix_arch}-${os}" + != system; + + service = + if !needsCrossCompilation + then + packages.${service_name}.overrideAttrs + (old: old // {doCheck = false;}) + else + packages.${service_name}.overrideAttrs (old: + old + // { + GOOS = os; + GOARCH = arch; + # CGO_ENABLED = disabled breaks the CLI compilation + # CGO_ENABLED = 0; + doCheck = false; + }); + in + builtins.trace "${service}/bin" pkgs.dockerTools.buildImage { + name = "${imageRegistry}/${service_name}"; + tag = "latest-${arch}"; + # tag = commit_hash; + created = "now"; + copyToRoot = pkgs.buildEnv { + name = "image-root"; + paths = [ + service + container_pkgs.bashInteractive + container_pkgs.nettools + container_pkgs.gnugrep + container_pkgs.coreutils + ]; + pathsToLink = ["/bin"]; + }; + architecture = arch; + config.Cmd = + if !needsCrossCompilation + then ["${service}/bin/${service.pname}"] + else ["${service}/bin/${os}_${arch}/${service.pname}"]; + }; + }; + }) { + arch = architectures; + service_name = service_names; + }; + in + pkgs.lib.foldl' (set: acc: pkgs.lib.recursiveUpdate acc set) {} + all; + }; + # Add containers matching architecture with local system as toplevel packages + # this means calling `nix build .#-container` will build the container matching the local system. + # For cross-compilation use the containers attribute directly: `nix build .containers...` + outputWithContaniers = pkgs.lib.foldl' mergeContainerPackages systemOutput service_names; + outputWithContainersAndPushers = pkgs.lib.foldl' multiPlatformDockerPusher outputWithContaniers service_names; + in + outputWithContainersAndPushers + ); +} diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 00000000..593405c4 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,143 @@ +cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg= +cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= +github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= +github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= +github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0 h1:t527LHHE3HmiHrq74QMpNPZpGCIJzTx+apLkMKt4HC0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= +github.com/bmatcuk/doublestar v1.1.1 h1:YroD6BJCZBYx06yYFEWvUuKVWQn3vLLQAVmDmvTSaiQ= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= +github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA= +github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4= +github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d h1:c93kUJDtVAXFEhsCh5jSxyOJmFHuzcihnslQiX8Urwo= +github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= +github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= +github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= +github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY= +github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 h1:Vx8kDVhO2qepK8w44lBtp+RzN3ld743i+LYPzODJSpQ= +github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9/go.mod h1:ldkoR3iXABBeqlTibQ3MYaviA1oSlPvim6f55biwBh4= +github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w= +github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY= +github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= +github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1 h1:0pHpWtx9vcvC0xGZqEQlQdfSQs7WRlAjuPvk3fOZDCo= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= +github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad h1:fiWzISvDn0Csy5H0iwgAuJGQTUpVfEMJJd4nRFXogbc= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA= +github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= +github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA= +github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2 h1:IRJeR9r1pYWsHKTRe/IInb7lYvbBVIqOgsX/u0mbOWY= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= +google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= +nullprogram.com/x/optparse v1.0.0 h1:xGFgVi5ZaWOnYdac2foDT3vg0ZZC9ErXFV57mr4OHrI= +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= diff --git a/kardinal-cli/cmd/root.go b/kardinal-cli/cmd/root.go new file mode 100644 index 00000000..8c8a8138 --- /dev/null +++ b/kardinal-cli/cmd/root.go @@ -0,0 +1,210 @@ +package cmd + +import ( + "context" + "encoding/json" + "fmt" + "log" + "net/http" + + "github.com/compose-spec/compose-go/cli" + "github.com/compose-spec/compose-go/types" + "github.com/spf13/cobra" + + api "github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api/api/golang/client" + api_types "github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api/api/golang/types" +) + +const ( + projectName = "kardinal" + devMode = true + kontrolServiceApiUrl = "ad718d90d54d54dd084dea50a9f011af-1140086995.us-east-1.elb.amazonaws.com" + kontrolServicePort = 8080 +) + +var composeFile string + +var rootCmd = &cobra.Command{ + Use: "kardinal", + Short: "Kardinal CLI to manage deployment flows", +} + +var flowCmd = &cobra.Command{ + Use: "flow", + Short: "Manage deployment flows", +} + +var deployCmd = &cobra.Command{ + Use: "deploy", + Short: "Deploy services", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + services, err := parseComposeFile(composeFile) + if err != nil { + log.Fatalf("Error loading compose file: %v", err) + } + deploy(services) + }, +} + +var createCmd = &cobra.Command{ + Use: "create [service name] [image name]", + Short: "Create a new service in development mode", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + serviceName, imageName := args[0], args[1] + services, err := parseComposeFile(composeFile) + if err != nil { + log.Fatalf("Error loading compose file: %v", err) + } + + fmt.Printf("Creating service %s with image %s in development mode...\n", serviceName, imageName) + createDevFlow(services, imageName, serviceName) + }, +} + +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete services", + Args: cobra.ExactArgs(0), + Run: func(cmd *cobra.Command, args []string) { + services, err := parseComposeFile(composeFile) + if err != nil { + log.Fatalf("Error loading compose file: %v", err) + } + deleteFlow(services) + + fmt.Print("Deleting dev flow") + }, +} + +func init() { + rootCmd.AddCommand(flowCmd) + rootCmd.AddCommand(deployCmd) + flowCmd.AddCommand(createCmd, deleteCmd) + + flowCmd.PersistentFlags().StringVarP(&composeFile, "docker-compose", "d", "", "Path to the Docker Compose file") + flowCmd.MarkPersistentFlagRequired("docker-compose") + deployCmd.PersistentFlags().StringVarP(&composeFile, "docker-compose", "d", "", "Path to the Docker Compose file") + deployCmd.MarkPersistentFlagRequired("docker-compose") +} + +func Execute() error { + return rootCmd.Execute() +} + +func loadComposeFile(filename string) (*types.Project, error) { + opts, err := cli.NewProjectOptions([]string{filename}, + cli.WithOsEnv, + cli.WithDotEnv, + cli.WithName(projectName), + ) + if err != nil { + return nil, err + } + + project, err := cli.ProjectFromOptions(opts) + if err != nil { + return nil, err + } + + return project, nil +} + +func parseComposeFile(composeFile string) ([]types.ServiceConfig, error) { + project, err := loadComposeFile(composeFile) + if err != nil { + log.Fatalf("Error loading compose file: %v", err) + return nil, err + } + + fmt.Println("Services in the Docker Compose file:") + for _, service := range project.Services { + fmt.Println(service.Name) + } + + projectYAML, err := project.MarshalJSON() + if err != nil { + log.Fatal(err) + return nil, err + } + + var dockerCompose map[string]interface{} + err = json.Unmarshal(projectYAML, &dockerCompose) + if err != nil { + log.Fatalf("error: %v", err) + return nil, err + } + + return project.Services, nil +} + +func createDevFlow(services []types.ServiceConfig, imageLocator, serviceName string) { + ctx := context.Background() + + // fmt.Printf("Services:\n%v", services) + // fmt.Printf("%v", serviceName) + // fmt.Printf("%v", imageLocator) + body := api_types.PostFlowCreateJSONRequestBody{ + DockerCompose: &services, + ServiceName: &serviceName, + ImageLocator: &imageLocator, + } + client := getKontrolServiceClient() + + resp, err := client.PostFlowCreateWithResponse(ctx, body) + if err != nil { + log.Fatalf("Failed to create dev flow: %v", err) + } + + fmt.Printf("Response: %s\n", string(resp.Body)) + fmt.Printf("Response: %s\n", resp) +} + +func deploy(services []types.ServiceConfig) { + ctx := context.Background() + + body := api_types.PostDeployJSONRequestBody{ + DockerCompose: &services, + } + client := getKontrolServiceClient() + + resp, err := client.PostDeployWithResponse(ctx, body) + if err != nil { + log.Fatalf("Failed to deploy: %v", err) + } + + fmt.Printf("Response: %s\n", string(resp.Body)) +} + +func deleteFlow(services []types.ServiceConfig) { + ctx := context.Background() + + body := api_types.PostFlowDeleteJSONRequestBody{ + DockerCompose: &services, + } + client := getKontrolServiceClient() + + resp, err := client.PostFlowDeleteWithResponse(ctx, body) + if err != nil { + log.Fatalf("Failed to delete flow: %v", err) + } + + fmt.Printf("Response: %s\n", string(resp.Body)) +} + +func getKontrolServiceClient() *api.ClientWithResponses { + if devMode { + client, err := api.NewClientWithResponses("http://localhost:8080", api.WithHTTPClient(http.DefaultClient)) + if err != nil { + log.Fatalf("Failed to create client: %v", err) + } + return client + } else { + client, err := api.NewClientWithResponses(fmt.Sprintf("http://%s:%v", kontrolServiceApiUrl, kontrolServicePort)) + if err != nil { + log.Fatalf("Failed to create client: %v", err) + } + return client + } +} diff --git a/kardinal-cli/default.nix b/kardinal-cli/default.nix new file mode 100644 index 00000000..5aafcd44 --- /dev/null +++ b/kardinal-cli/default.nix @@ -0,0 +1,20 @@ +{ + pkgs, + commit_hash ? "dirty", +}: let + pname = "kardinal.cli"; + ldflags = pkgs.lib.concatStringsSep "\n" [ + "-X github.com/kurtosis-tech/kurtosis/kardinal.AppName=${pname}" + "-X github.com/kurtosis-tech/kurtosis/kardinal.Commit=${commit_hash}" + ]; +in + pkgs.buildGoApplication { + # pname has to match the location (folder) where the main function is or use + # subPackges to specify the file (e.g. subPackages = ["some/folder/main.go"];) + inherit pname ldflags; + name = "${pname}"; + pwd = ./.; + src = ./.; + modules = ./gomod2nix.toml; + CGO_ENABLED = 0; + } diff --git a/kardinal-cli/go.mod b/kardinal-cli/go.mod new file mode 100644 index 00000000..b2f9cc98 --- /dev/null +++ b/kardinal-cli/go.mod @@ -0,0 +1,41 @@ +module kardinal.cli + +go 1.22 + +require ( + github.com/compose-spec/compose-go v1.20.2 + github.com/spf13/cobra v1.8.0 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/oapi-codegen/runtime v1.1.1 // indirect +) + +replace github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api => ../libs/cli-kontrol-api + +require ( + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api v0.0.0 + github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/kardinal-cli/go.sum b/kardinal-cli/go.sum new file mode 100644 index 00000000..56f64843 --- /dev/null +++ b/kardinal-cli/go.sum @@ -0,0 +1,83 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ= +github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= diff --git a/kardinal-cli/gomod2nix.toml b/kardinal-cli/gomod2nix.toml new file mode 100644 index 00000000..413de668 --- /dev/null +++ b/kardinal-cli/gomod2nix.toml @@ -0,0 +1,561 @@ +schema = 3 + +[mod] + [mod."cloud.google.com/go/compute"] + version = "v1.20.1" + hash = "sha256-HQvSLdoSagQsuqOb/D9+xMc8nE9Y44Z30rQgd2AGEr8=" + [mod."cloud.google.com/go/compute/metadata"] + version = "v0.2.3" + hash = "sha256-kYB1FTQRdTDqCqJzSU/jJYbVUGyxbkASUKbEs36FUyU=" + [mod."github.com/BurntSushi/toml"] + version = "v1.3.2" + hash = "sha256-FIwyH67KryRWI9Bk4R8s1zFP0IgKR4L66wNQJYQZLeg=" + [mod."github.com/CloudyKit/fastprinter"] + version = "v0.0.0-20200109182630-33d98a066a53" + hash = "sha256-KUuNS6OlaDUfpKIvZWJr8fiUR8ki/hUwMZiunNj0cxo=" + [mod."github.com/CloudyKit/jet/v6"] + version = "v6.2.0" + hash = "sha256-22PSPgN9ajVbm0gbhnrPWgroacipQaL7AMLsECzzd7A=" + [mod."github.com/Joker/jade"] + version = "v1.1.3" + hash = "sha256-264xyHGlF/hqJGY28YAFFh4VcsoJ0W/5HrgcJAdLKZs=" + [mod."github.com/NYTimes/gziphandler"] + version = "v0.0.0-20170623195520-56545f4a5d46" + hash = "sha256-4mTVrxEH1Cu3MVhm/nB+Zm8b2oYS4SecOHjnbT5Pk7s=" + [mod."github.com/RaveNoX/go-jsoncommentstrip"] + version = "v1.0.0" + hash = "sha256-bD0Jb4TGkJhuJ1MGptK+eTpGseRom1QRO9hwxnoF60U=" + [mod."github.com/Shopify/goreferrer"] + version = "v0.0.0-20220729165902-8cddb4f5de06" + hash = "sha256-zyP8NdtP79I7DOH7bUw54pY5ge1yWkSIbh4jnp8gel4=" + [mod."github.com/andybalholm/brotli"] + version = "v1.0.5" + hash = "sha256-/qS8wU8yZQJ+uTOg66rEl9s7spxq9VIXF5L1BcaEClc=" + [mod."github.com/apapsch/go-jsonmerge/v2"] + version = "v2.0.0" + hash = "sha256-xp/1B6XUN2EbddBfoUkTV3oTk+34m4kOZP+66HhfLg4=" + [mod."github.com/armon/go-socks5"] + version = "v0.0.0-20160902184237-e75332964ef5" + hash = "sha256-2F/mqTbr7jhtlZ0UGMRfHrjXbHbGwTJi951y4CQInIA=" + [mod."github.com/asaskevich/govalidator"] + version = "v0.0.0-20190424111038-f61b66f89f4a" + hash = "sha256-pcrINvdGpQExaSQv1j19L0NLWcvZL8jXQMsvMlVq8ss=" + [mod."github.com/aymerick/douceur"] + version = "v0.2.0" + hash = "sha256-NiBX8EfOvLXNiK3pJaZX4N73YgfzdrzRXdiBFe3X3sE=" + [mod."github.com/bmatcuk/doublestar"] + version = "v1.1.1" + hash = "sha256-mcR+gZ11pja98RwW7eN6T6TwC07ujPmQ8CcyH45r//0=" + [mod."github.com/bsm/ginkgo/v2"] + version = "v2.12.0" + hash = "sha256-rZ9AF2RPWwQcgFmh3bmUXDaTLL14fQfPtv+DuC8xqzQ=" + [mod."github.com/bsm/gomega"] + version = "v1.27.10" + hash = "sha256-EltjBddI8lJOxiONk2GgbjtHp7ysV00CK7BRVbAOLZ4=" + [mod."github.com/bytedance/sonic"] + version = "v1.11.6" + hash = "sha256-oGDdBbAHDwQYFFcg3AeYaHwOGQNa1q4n/0w4y2eqsxk=" + [mod."github.com/bytedance/sonic/loader"] + version = "v0.1.1" + hash = "sha256-9MzO8LYUrun40uXTexcbKtQO1MvXhlD6Er6T6v9TOtE=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.3.0" + hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY=" + [mod."github.com/chenzhuoyu/base64x"] + version = "v0.0.0-20230717121745-296ad89f973d" + hash = "sha256-o1qbpdkfbXE/DWW1ZFgTLPS2HGS0Ib69dbd6zO8CCWk=" + [mod."github.com/chenzhuoyu/iasm"] + version = "v0.9.0" + hash = "sha256-xlZIAcRAD9dufk7JZfyKyiBzw6Gzfj4oKh2wbjKukQg=" + [mod."github.com/cloudwego/base64x"] + version = "v0.1.4" + hash = "sha256-umCZR3iNmHFm+BC76kfpdcRG+pTQd6Jcu/c2kQDnyfw=" + [mod."github.com/cloudwego/iasm"] + version = "v0.2.0" + hash = "sha256-TzIP2N3HOesXrKACsRr/ShcoqttwPGZPckIepsTyHOA=" + [mod."github.com/compose-spec/compose-go"] + version = "v1.20.2" + hash = "sha256-uICczxxRYLAW4jMHEsNxcOUBoCT5l5AMSrexHJ3xTcA=" + [mod."github.com/cpuguy83/go-md2man/v2"] + version = "v2.0.3" + hash = "sha256-FAMxR5eBO9LQp6ev1b7zaPUS5aoNz1GtsPpoArjiJVw=" + [mod."github.com/creack/pty"] + version = "v1.1.9" + hash = "sha256-Rj6c4/3IApJcS36iPVIEdlMSC/SWmywnpqk1500ik5k=" + [mod."github.com/davecgh/go-spew"] + version = "v1.1.1" + hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI=" + [mod."github.com/deepmap/oapi-codegen/v2"] + version = "v2.2.1-0.20240604070534-2f0ff757704b" + hash = "sha256-xexjMUdpXoOS9i8dA3MD4mBFHcXSnk9y6iKZfeSXjHg=" + [mod."github.com/dgryski/go-rendezvous"] + version = "v0.0.0-20200823014737-9f7001d12a5f" + hash = "sha256-n/7xo5CQqo4yLaWMSzSN1Muk/oqK6O5dgDOFWapeDUI=" + [mod."github.com/distribution/reference"] + version = "v0.5.0" + hash = "sha256-nTlqurp/J/xOZeR0JcJMmE4GUHMMd5I+EOR7huzYuok=" + [mod."github.com/docker/go-connections"] + version = "v0.4.0" + hash = "sha256-GHNIjOuuNp5lFQ308+nDNwQPGESCVV7bCUxSW5ZxZlc=" + [mod."github.com/docker/go-units"] + version = "v0.5.0" + hash = "sha256-iK/V/jJc+borzqMeqLY+38Qcts2KhywpsTk95++hImE=" + [mod."github.com/emicklei/go-restful/v3"] + version = "v3.11.0" + hash = "sha256-Kp5ndPvj1PhK0nscM1pNNK2Q4ahUuLED/baEgL9pKNo=" + [mod."github.com/evanphx/json-patch"] + version = "v5.6.0+incompatible" + hash = "sha256-F4hI+H6oWFCKa47+x74M4fqcwDtv3wXOLGVxqDn0F5c=" + [mod."github.com/fatih/structs"] + version = "v1.1.0" + hash = "sha256-OCmubTLF1anwNnkvFZDYHnF6hFlX0WDoe/9+dDlaMPM=" + [mod."github.com/felixge/fgprof"] + version = "v0.9.3" + hash = "sha256-Q0EOEvkwqNbB/yR85MGrbzoahGbWbSw8ISmP0KTdAw8=" + [mod."github.com/flosch/pongo2/v4"] + version = "v4.0.2" + hash = "sha256-MQa2y64XpNPa3dKEerYJT5eFTrAk7flts9h2LG1QQQY=" + [mod."github.com/fxamacker/cbor/v2"] + version = "v2.6.0" + hash = "sha256-8EMjmc2FYVb0OXuzU1FWkSqYEtJjYdIT2PWsChoiTyQ=" + [mod."github.com/gabriel-vasile/mimetype"] + version = "v1.4.3" + hash = "sha256-EDmlRi3av27dq/ISVTglv08z4yZzMQ/SxL1c46EJro0=" + [mod."github.com/getkin/kin-openapi"] + version = "v0.125.0" + hash = "sha256-4g7MoGIyH80ek9SEqHYV1TjsYAx+wTzvipEKhfddGYA=" + [mod."github.com/gin-contrib/sse"] + version = "v0.1.0" + hash = "sha256-zYbMTao+1F+385Lvsba9roLmmt9eYqr57sUWo0LCVhw=" + [mod."github.com/gin-gonic/gin"] + version = "v1.10.0" + hash = "sha256-esJasHrJtuTBwGPGAoc/XSb428J8va+tPGcZ0gTfsgc=" + [mod."github.com/go-logr/logr"] + version = "v1.4.1" + hash = "sha256-WM4badoqxXlBmqCRrnmtNce63dLlr/FJav3BJSYHvaY=" + [mod."github.com/go-openapi/jsonpointer"] + version = "v0.20.2" + hash = "sha256-z9IZxP+JvJ1WvrHE7qbAZQqJ3XMx1uD0S611shTMna8=" + [mod."github.com/go-openapi/jsonreference"] + version = "v0.20.2" + hash = "sha256-klWZKK7LZqSg3HMIrSkjh/NwaZTr+8kTW2ok2+JlioE=" + [mod."github.com/go-openapi/swag"] + version = "v0.22.8" + hash = "sha256-YMJpCbWT9ABlCmuLjxQRwhddlYYqDBWcyDIdnV8bAMc=" + [mod."github.com/go-playground/assert/v2"] + version = "v2.2.0" + hash = "sha256-jBDvfGBS2EWzN6Ve+ZU2TyAj3c2Wqbxw88kz2NsBq44=" + [mod."github.com/go-playground/locales"] + version = "v0.14.1" + hash = "sha256-BMJGAexq96waZn60DJXZfByRHb8zA/JP/i6f/YrW9oQ=" + [mod."github.com/go-playground/universal-translator"] + version = "v0.18.1" + hash = "sha256-2/B2qP51zfiY+k8G0w0D03KXUc7XpWj6wKY7NjNP/9E=" + [mod."github.com/go-playground/validator/v10"] + version = "v10.20.0" + hash = "sha256-FKF+ebrSedNdh5Wq8aE8UP+5LiM8B28bk8v3gyqqdDk=" + [mod."github.com/go-task/slim-sprig"] + version = "v0.0.0-20230315185526-52ccab3ef572" + hash = "sha256-D6NjCQbcYC53NdwzyAm4i9M1OjTJIVu4EIt3AD/Vxfg=" + [mod."github.com/go-test/deep"] + version = "v1.0.8" + hash = "sha256-AKOpW32qcq1BTVEPC6MshiFA3ROucnmlJMIfvwYXvNY=" + [mod."github.com/goccy/go-json"] + version = "v0.10.2" + hash = "sha256-6fMD2/Rku8HT0zDdeA23pX0YxbohiIOC8OJNYbylJTQ=" + [mod."github.com/gogo/protobuf"] + version = "v1.3.2" + hash = "sha256-pogILFrrk+cAtb0ulqn9+gRZJ7sGnnLLdtqITvxvG6c=" + [mod."github.com/golang-jwt/jwt"] + version = "v3.2.2+incompatible" + hash = "sha256-LOkpuXhWrFayvVf1GOaOmZI5YKEsgqVSb22aF8LnCEM=" + [mod."github.com/golang/groupcache"] + version = "v0.0.0-20210331224755-41bb18bfe9da" + hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" + [mod."github.com/golang/protobuf"] + version = "v1.5.4" + hash = "sha256-N3+Lv9lEZjrdOWdQhFj6Y3Iap4rVLEQeI8/eFFyAMZ0=" + [mod."github.com/golang/snappy"] + version = "v0.0.4" + hash = "sha256-Umx+5xHAQCN/Gi4HbtMhnDCSPFAXSsjVbXd8n5LhjAA=" + [mod."github.com/gomarkdown/markdown"] + version = "v0.0.0-20230922112808-5421fefb8386" + hash = "sha256-N3qjdMq0L4toiTu2rut9sSPR/cA7bEe/bZ1iEVNzXOE=" + [mod."github.com/google/btree"] + version = "v1.0.1" + hash = "sha256-1PIeFGgUL4BK/StL/D12pg9bEQ5HfMT/fMLdus4pZTs=" + [mod."github.com/google/gnostic-models"] + version = "v0.6.8" + hash = "sha256-YzA/XpvPyfdplJtHmAUdQk9P+j0NBwHhW9nj1DaGaoQ=" + [mod."github.com/google/go-cmp"] + version = "v0.6.0" + hash = "sha256-qgra5jze4iPGP0JSTVeY5qV5AvEnEu39LYAuUCIkMtg=" + [mod."github.com/google/gofuzz"] + version = "v1.2.0" + hash = "sha256-T6Gz741l45L3F6Dt7fiAuQvQQg59Qtap3zG05M2cfqU=" + [mod."github.com/google/pprof"] + version = "v0.0.0-20211214055906-6f57359322fd" + hash = "sha256-Ju5Ke03QOM0YbubQT8jPPcjJq2mgpua8RmqB1UUSLAw=" + [mod."github.com/google/uuid"] + version = "v1.6.0" + hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw=" + [mod."github.com/gorilla/css"] + version = "v1.0.0" + hash = "sha256-Mmt/IqHpgrtWpbr/AKcJyf/USQTqEuv1HVivY4eHzoQ=" + [mod."github.com/gorilla/mux"] + version = "v1.8.1" + hash = "sha256-nDABvAhlYgxUW2N/brrep7NkQXoSGcHhA+XI4+tK0F0=" + [mod."github.com/gorilla/websocket"] + version = "v1.5.0" + hash = "sha256-EYVgkSEMo4HaVrsWKqnsYRp8SSS8gNf7t+Elva02Ofc=" + [mod."github.com/gregjones/httpcache"] + version = "v0.0.0-20180305231024-9cad4c3443a7" + hash = "sha256-2ngFfFuSm8YSTNZWGQuN5yTpsXlwY2R8aaIzjDnjTXI=" + [mod."github.com/imdario/mergo"] + version = "v0.3.16" + hash = "sha256-gh2TEAq8YrZOEAf6SFW4AIcEEUguD68G+7/VUnBeWwM=" + [mod."github.com/inconshreveable/mousetrap"] + version = "v1.1.0" + hash = "sha256-XWlYH0c8IcxAwQTnIi6WYqq44nOKUylSWxWO/vi+8pE=" + [mod."github.com/invopop/yaml"] + version = "v0.2.0" + hash = "sha256-RxeDuvwOSWYaLc8Q7T39rfFT3bZX3g9Bu0RFwxH6sLw=" + [mod."github.com/iris-contrib/schema"] + version = "v0.0.6" + hash = "sha256-2jk46kocpQGmswalwwFI5P8B4AHR1xkTnbPdtFa9VFo=" + [mod."github.com/josharian/intern"] + version = "v1.0.0" + hash = "sha256-LJR0QE2vOQ2/2shBbO5Yl8cVPq+NFrE3ers0vu9FRP0=" + [mod."github.com/json-iterator/go"] + version = "v1.1.12" + hash = "sha256-To8A0h+lbfZ/6zM+2PpRpY3+L6725OPC66lffq6fUoM=" + [mod."github.com/juju/gnuflag"] + version = "v0.0.0-20171113085948-2ce1bb71843d" + hash = "sha256-D66Gq0FC0k+86qOMZrpEsQ+6HzqkBibCdHXZsN1BfmY=" + [mod."github.com/kataras/blocks"] + version = "v0.0.7" + hash = "sha256-sXTcBIPuifzEgqe8m0Oe45A19egRGY1C+ZndaKWxxuY=" + [mod."github.com/kataras/golog"] + version = "v0.1.9" + hash = "sha256-o/l8xq0bNRtjBbnCTjiIg4K2SXP3w1a+df9nBVI3VWQ=" + [mod."github.com/kataras/iris/v12"] + version = "v12.2.6-0.20230908161203-24ba4e8933b9" + hash = "sha256-49LV2uv5+jKCANxIMTdDp7i++kWlp91oiFhnPvnVkQM=" + [mod."github.com/kataras/pio"] + version = "v0.0.12" + hash = "sha256-+DnDBEQde7iLVBE9XQmEhYpR6YTDeVplniQWrZH964w=" + [mod."github.com/kataras/sitemap"] + version = "v0.0.6" + hash = "sha256-NmAX6wCT1Z5n44O5TId4iovDUxH/ukgLKEGmchAMS8Q=" + [mod."github.com/kataras/tunnel"] + version = "v0.0.4" + hash = "sha256-yhCeH7eM8F+BxOy/y5MyAI0F0ghfLFUB/5aemIBZM54=" + [mod."github.com/kisielk/errcheck"] + version = "v1.5.0" + hash = "sha256-ZmocFXtg+Thdup+RqDYC/Td3+m1nS0FydZecfsWXIzI=" + [mod."github.com/kisielk/gotool"] + version = "v1.0.0" + hash = "sha256-lsdQkue8gFz754PGqczUnvGiCQq87SruQtdrDdQVTpE=" + [mod."github.com/klauspost/compress"] + version = "v1.16.7" + hash = "sha256-8miX/lnXyNLPSqhhn5BesLauaIAxETpQpWtr1cu2f+0=" + [mod."github.com/klauspost/cpuid/v2"] + version = "v2.2.7" + hash = "sha256-bjinp7b7qWk+DcZDDv1EedJxZqGxp2NWY+NYKBfE5xU=" + [mod."github.com/knz/go-libedit"] + version = "v1.10.1" + hash = "sha256-9QLTNZPo8qq+6JhvlTbETNqx1SFrv7l9YLH1kQEF+CE=" + [mod."github.com/kr/pretty"] + version = "v0.3.1" + hash = "sha256-DlER7XM+xiaLjvebcIPiB12oVNjyZHuJHoRGITzzpKU=" + [mod."github.com/kr/pty"] + version = "v1.1.1" + hash = "sha256-AVeS+ivwNzIrgWQaLtsmO2f2MYGpxIVqdac/EzaYI1Q=" + [mod."github.com/kr/text"] + version = "v0.2.0" + hash = "sha256-fadcWxZOORv44oak3jTxm6YcITcFxdGt4bpn869HxUE=" + [mod."github.com/kurtosis-tech/stacktrace"] + version = "v0.0.0-20211028211901-1c67a77b5409" + hash = "sha256-xm9l7tlCb0U25WJvByFikWxnAmOwTmOtlSDBdbyDMMY=" + [mod."github.com/labstack/echo/v4"] + version = "v4.12.0" + hash = "sha256-TPXJv/6C53bnmcEYxa9g5Mft8u/rLT96q64tQ9+RtKU=" + [mod."github.com/labstack/gommon"] + version = "v0.4.2" + hash = "sha256-395+BETDpv15L2lsCiEccwakXgEQxKHlYBAU0Ot3qhY=" + [mod."github.com/leodido/go-urn"] + version = "v1.4.0" + hash = "sha256-Q6kplWkY37Tzy6GOme3Wut40jFK4Izun+ij/BJvcEu0=" + [mod."github.com/mailgun/raymond/v2"] + version = "v2.0.48" + hash = "sha256-HC2vbTL9eCgk1m00h6Mg6W/pu6n/Y7Qr4MJuVQIX4v0=" + [mod."github.com/mailru/easyjson"] + version = "v0.7.7" + hash = "sha256-NVCz8MURpxgOjHXqxOZExqV4bnpHggpeAOyZDArjcy4=" + [mod."github.com/mattn/go-colorable"] + version = "v0.1.13" + hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8=" + [mod."github.com/mattn/go-isatty"] + version = "v0.0.20" + hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ=" + [mod."github.com/mattn/go-shellwords"] + version = "v1.0.12" + hash = "sha256-H7sLKwLzQmcrkEa4SpkHFSpkrpWmX7foOGiKswBUdhs=" + [mod."github.com/microcosm-cc/bluemonday"] + version = "v1.0.25" + hash = "sha256-/crG5s6cDrJ55nkDBwugLUpY7U+vQuHpCkKm7nnN8Zc=" + [mod."github.com/mitchellh/mapstructure"] + version = "v1.5.0" + hash = "sha256-ztVhGQXs67MF8UadVvG72G3ly0ypQW0IRDdOOkjYwoE=" + [mod."github.com/moby/spdystream"] + version = "v0.2.0" + hash = "sha256-Feme5UoWuBCvnLPKw1ozKkF3SM7PAjjPFQZ3TJhghR0=" + [mod."github.com/modern-go/concurrent"] + version = "v0.0.0-20180306012644-bacd9c7ef1dd" + hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo=" + [mod."github.com/modern-go/reflect2"] + version = "v1.0.2" + hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU=" + [mod."github.com/mohae/deepcopy"] + version = "v0.0.0-20170929034955-c48cc78d4826" + hash = "sha256-TQMmKqIYwVhmMVh4RYQkAui97Eyj7poLmcAuDcHXsEk=" + [mod."github.com/munnerz/goautoneg"] + version = "v0.0.0-20191010083416-a7dc8b61c822" + hash = "sha256-79URDDFenmGc9JZu+5AXHToMrtTREHb3BC84b/gym9Q=" + [mod."github.com/mxk/go-flowrate"] + version = "v0.0.0-20140419014527-cca7078d478f" + hash = "sha256-gRTfRfff/LRxC1SXXnQd2tV3UTcTx9qu90DJIVIaGn8=" + [mod."github.com/oapi-codegen/runtime"] + version = "v1.1.1" + hash = "sha256-GRvzpQngQPAy59KvvcwhjYqjx4gttIhOtQKjqfQcNvI=" + [mod."github.com/onsi/ginkgo/v2"] + version = "v2.15.0" + hash = "sha256-E+f2d0RZ6DSpdh4OasCbT1vGckXsziM3hFt4HkI5x1s=" + [mod."github.com/onsi/gomega"] + version = "v1.31.0" + hash = "sha256-a7Bmw1MpIgZNgWenBUEh/R/IpUFVUnDL4zaFznU0ILo=" + [mod."github.com/opencontainers/go-digest"] + version = "v1.0.0" + hash = "sha256-cfVDjHyWItmUGZ2dzQhCHgmOmou8v7N+itDkLZVkqkQ=" + [mod."github.com/pelletier/go-toml/v2"] + version = "v2.2.2" + hash = "sha256-ukxk1Cfm6cQW18g/aa19tLcUu5BnF7VmfAvrDHAOl6A=" + [mod."github.com/perimeterx/marshmallow"] + version = "v1.1.5" + hash = "sha256-fFWjg0FNohDSV0/wUjQ8fBq1g8h6yIqTrHkxqL2Tt0s=" + [mod."github.com/peterbourgon/diskv"] + version = "v2.0.1+incompatible" + hash = "sha256-K4mEVjH0eyxyYHQRxdbmgJT0AJrfucUwGB2BplRRt9c=" + [mod."github.com/pkg/diff"] + version = "v0.0.0-20210226163009-20ebb0f2a09e" + hash = "sha256-0aP4CtvBp9lmxoIvKq6mrOyQidLubH1bG92RD/n7bbw=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/pkg/profile"] + version = "v1.7.0" + hash = "sha256-wd8L8WiPoojo/oVUshgiJdM/k367LL1XO5BL5u1L2UU=" + [mod."github.com/pmezard/go-difflib"] + version = "v1.0.0" + hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA=" + [mod."github.com/redis/go-redis/v9"] + version = "v9.5.3" + hash = "sha256-XqJGBm6y6PPo5pxRsmDX4TGMp62V+4kDHBqOVNOZFjA=" + [mod."github.com/rogpeppe/go-internal"] + version = "v1.12.0" + hash = "sha256-qvDNCe3l84/LgrA8X4O15e1FeDcazyX91m9LmXGXX6M=" + [mod."github.com/russross/blackfriday/v2"] + version = "v2.1.0" + hash = "sha256-R+84l1si8az5yDqd5CYcFrTyNZ1eSYlpXKq6nFt4OTQ=" + [mod."github.com/samber/lo"] + version = "v1.39.0" + hash = "sha256-bFJXbzFpUQM2xoNrHrlzn0RJZujd21H00zGBgo/JEb0=" + [mod."github.com/schollz/closestmatch"] + version = "v2.1.0+incompatible" + hash = "sha256-SpWqGfqlMkZPQ6TSf7NTaYMbQllBaBgPM8oxTBOTn7w=" + [mod."github.com/sirupsen/logrus"] + version = "v1.9.3" + hash = "sha256-EnxsWdEUPYid+aZ9H4/iMTs1XMvCLbXZRDyvj89Ebms=" + [mod."github.com/spf13/cobra"] + version = "v1.8.0" + hash = "sha256-oAE+fEaRfZPE541IPWE0GMeBBYgH2DMhtZNxzp7DFlY=" + [mod."github.com/spf13/pflag"] + version = "v1.0.5" + hash = "sha256-w9LLYzxxP74WHT4ouBspH/iQZXjuAh2WQCHsuvyEjAw=" + [mod."github.com/spkg/bom"] + version = "v0.0.0-20160624110644-59b7046e48ad" + hash = "sha256-HifA8ur35a/T83Xdaw9vR9EP2SssigF2DG3h1+wJoGA=" + [mod."github.com/stretchr/objx"] + version = "v0.5.2" + hash = "sha256-VKYxrrFb1nkX6Wu3tE5DoP9+fCttwSl9pgLN6567nck=" + [mod."github.com/stretchr/testify"] + version = "v1.9.0" + hash = "sha256-uUp/On+1nK+lARkTVtb5RxlW15zxtw2kaAFuIASA+J0=" + [mod."github.com/tdewolff/minify/v2"] + version = "v2.12.9" + hash = "sha256-eSyTz73LOBXGAMFDJ1/CtmuMiRDzC4HkB+0xXtpvafk=" + [mod."github.com/tdewolff/parse/v2"] + version = "v2.6.8" + hash = "sha256-uimZjGFbVz/xiaDHPnbqrEmqeyQF+x5Gp3PfdCsD710=" + [mod."github.com/tidwall/btree"] + version = "v1.7.0" + hash = "sha256-bnr6c7a0nqo2HyGqxHk0kEZCEsjLYkPbAVY9WzaZ30o=" + [mod."github.com/tidwall/match"] + version = "v1.1.1" + hash = "sha256-M2klhPId3Q3T3VGkSbOkYl/2nLHnsG+yMbXkPkyrRdg=" + [mod."github.com/tidwall/redcon"] + version = "v1.6.2" + hash = "sha256-EhMOHoYAucg7ahRc9ALiEOluvJCAVd9nwy7BKfDt6bc=" + [mod."github.com/twitchyliquid64/golang-asm"] + version = "v0.15.1" + hash = "sha256-HLk6oUe7EoITrNvP0y8D6BtIgIcmDZYtb/xl/dufIoY=" + [mod."github.com/ugorji/go/codec"] + version = "v1.2.12" + hash = "sha256-sp1LJ93UK7mFwgZqG8jxCgTCPgKR74HNU6XxX0Jfjm0=" + [mod."github.com/valyala/bytebufferpool"] + version = "v1.0.0" + hash = "sha256-I9FPZ3kCNRB+o0dpMwBnwZ35Fj9+ThvITn8a3Jr8mAY=" + [mod."github.com/valyala/fasttemplate"] + version = "v1.2.2" + hash = "sha256-gp+lNXE8zjO+qJDM/YbS6V43HFsYP6PKn4ux1qa5lZ0=" + [mod."github.com/vmihailenco/msgpack/v5"] + version = "v5.3.5" + hash = "sha256-Uj5xRZbtxE8evniQPuBVkxkEdMa/H/pt9s84J16swm8=" + [mod."github.com/vmihailenco/tagparser/v2"] + version = "v2.0.0" + hash = "sha256-M9QyaKhSmmYwsJk7gkjtqu9PuiqZHSmTkous8VWkWY0=" + [mod."github.com/x448/float16"] + version = "v0.8.4" + hash = "sha256-VKzMTMS9pIB/cwe17xPftCSK9Mf4Y6EuBEJlB4by5mE=" + [mod."github.com/xeipuuv/gojsonpointer"] + version = "v0.0.0-20180127040702-4e3ac2762d5f" + hash = "sha256-OEW9nOR3EeMzvqvqpdAgowpZlbXPLWM0JT+5bwWOxo8=" + [mod."github.com/xeipuuv/gojsonreference"] + version = "v0.0.0-20180127040603-bd5ef7bd5415" + hash = "sha256-ZbXA+ASQrTgBQzasUKC9vznrOGpquYyWr+uwpm46fvU=" + [mod."github.com/xeipuuv/gojsonschema"] + version = "v1.2.0" + hash = "sha256-1ERBEvxj3pvHkMS2mvmvmYRi8jgAaTquo5hwjDLAEdc=" + [mod."github.com/yosssi/ace"] + version = "v0.0.5" + hash = "sha256-0HnNZUypGGoQxFX214/eF7RJ9KxYpb56Q5Rn2tryOLM=" + [mod."github.com/yuin/goldmark"] + version = "v1.4.13" + hash = "sha256-GVwFKZY6moIS6I0ZGuio/WtDif+lkZRfqWS6b4AAJyI=" + [mod."golang.org/x/arch"] + version = "v0.8.0" + hash = "sha256-zz9sbr+yT6eqjHVlVBfDZVmIkzOT6DZFpN3eaQT4Afw=" + [mod."golang.org/x/crypto"] + version = "v0.23.0" + hash = "sha256-6hZjb/OazWFBef0C/aH63l49YQnzCh2vpIduzyfSSG8=" + [mod."golang.org/x/exp"] + version = "v0.0.0-20230713183714-613f0c0eb8a1" + hash = "sha256-VLE9CCOYpTdyBWaQ1YxXpGOBS74wpIR3JJ+JVzNyEkQ=" + [mod."golang.org/x/mod"] + version = "v0.17.0" + hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU=" + [mod."golang.org/x/net"] + version = "v0.25.0" + hash = "sha256-IjFfXLYNj27WLF7vpkZ6mfFXBnp+7QER3OQ0RgjxN54=" + [mod."golang.org/x/oauth2"] + version = "v0.16.0" + hash = "sha256-fJfS9dKaq82WaYSVWHMnxNLWH8+L4aip/C1AfJi4FFI=" + [mod."golang.org/x/sync"] + version = "v0.7.0" + hash = "sha256-2ETllEu2GDWoOd/yMkOkLC2hWBpKzbVZ8LhjLu0d2A8=" + [mod."golang.org/x/sys"] + version = "v0.20.0" + hash = "sha256-mowlaoG2k4n1c1rApWef5EMiXd3I77CsUi8jPh6pTYA=" + [mod."golang.org/x/telemetry"] + version = "v0.0.0-20240228155512-f48c80bd79b2" + hash = "sha256-31mlnHWpwrXwsKBXdvFVphcACQ/DQ7tv1AGBxL6Cs4U=" + [mod."golang.org/x/term"] + version = "v0.20.0" + hash = "sha256-kU+OVJbYktTIn4ZTAdomsOjL069Vj45sdroEMRKaRDI=" + [mod."golang.org/x/text"] + version = "v0.15.0" + hash = "sha256-pBnj0AEkfkvZf+3bN7h6epCD2kurw59clDP7yWvxKlk=" + [mod."golang.org/x/time"] + version = "v0.5.0" + hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU=" + [mod."golang.org/x/tools"] + version = "v0.21.0" + hash = "sha256-TU0gAxUX410AYc/nMxxZiaqXeORih1cXbKh3sxKufVg=" + [mod."golang.org/x/xerrors"] + version = "v0.0.0-20220907171357-04be3eba64a2" + hash = "sha256-6+zueutgefIYmgXinOflz8qGDDDj0Zhv+2OkGhBTKno=" + [mod."google.golang.org/appengine"] + version = "v1.6.8" + hash = "sha256-decMa0MiWfW/Bzr8QPPzzpeya0YWGHhZAt4Cr/bD1wQ=" + [mod."google.golang.org/genproto/googleapis/api"] + version = "v0.0.0-20240318140521-94a12d6c2237" + hash = "sha256-Tl2ABoESriYlPfeawE9xd5gPQYGzW4j/Il6gXEw33f0=" + [mod."google.golang.org/genproto/googleapis/rpc"] + version = "v0.0.0-20240314234333-6e1732d8331c" + hash = "sha256-P5SBku16dYnK4koUQxTeGwPxAAWH8rxbDm2pOzFLo/Q=" + [mod."google.golang.org/grpc"] + version = "v1.62.1" + hash = "sha256-1su6X0YT7MUflrTJijbq1CiisADZHudEx5sJq01TEaE=" + [mod."google.golang.org/protobuf"] + version = "v1.34.1" + hash = "sha256-qnHqY6KLZiZDbTVTN6uzF4jedxROYlPCYHoiv6XI0sc=" + [mod."gopkg.in/check.v1"] + version = "v1.0.0-20201130134442-10cb98267c6c" + hash = "sha256-VlIpM2r/OD+kkyItn6vW35dyc0rtkJufA93rjFyzncs=" + [mod."gopkg.in/inf.v0"] + version = "v0.9.1" + hash = "sha256-z84XlyeWLcoYOvWLxPkPFgLkpjyb2Y4pdeGMyySOZQI=" + [mod."gopkg.in/ini.v1"] + version = "v1.67.0" + hash = "sha256-V10ahGNGT+NLRdKUyRg1dos5RxLBXBk1xutcnquc/+4=" + [mod."gopkg.in/yaml.v2"] + version = "v2.4.0" + hash = "sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0=" + [mod."gopkg.in/yaml.v3"] + version = "v3.0.1" + hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU=" + [mod."gotest.tools/v3"] + version = "v3.4.0" + hash = "sha256-mohKBcuKT29Illk/Gm7Bu5E8QxhRYr6sEGPn/oux65k=" + [mod."istio.io/api"] + version = "v1.22.1-0.20240524024004-b6815be0740d" + hash = "sha256-crSFKGbjxvJRenCJVCTc4bgVl4//h3jDugNMjmUSQM4=" + [mod."istio.io/client-go"] + version = "v1.22.1" + hash = "sha256-yBen7y8xbvpgTzUYxEUOBqwkJGHfVjqxFUxKrxkHdls=" + [mod."k8s.io/api"] + version = "v0.30.2" + hash = "sha256-sq8jdb0wnAIhTO/hactsJ34mRuUgEuUNOrKaQasbHpQ=" + [mod."k8s.io/apimachinery"] + version = "v0.30.2" + hash = "sha256-EBWG9PlSfRwMed/u6oF8ME8n44/U5HGBCacH+Mns2UI=" + [mod."k8s.io/client-go"] + version = "v0.29.0" + hash = "sha256-GuY06sFi5YaQ4h925hCgdzfIMUjTcKxupKP6h7RWI78=" + [mod."k8s.io/gengo/v2"] + version = "v2.0.0-20240228010128-51d4e06bde70" + hash = "sha256-igAJQwUliynE2iDbBYc2HONd744+r7o3mQtFyYDYLb4=" + [mod."k8s.io/klog/v2"] + version = "v2.120.1" + hash = "sha256-PU3q5ODCHEO8z//zUKsCq2tky6ZNx4xFl3nHSPJpuW8=" + [mod."k8s.io/kube-openapi"] + version = "v0.0.0-20240228011516-70dd3763d340" + hash = "sha256-2BUbi2eNJFSTcmLyVYBYBe9Lhi0TVJoH2zVQlQ9eZIA=" + [mod."k8s.io/utils"] + version = "v0.0.0-20230726121419-3b25d923346b" + hash = "sha256-r8VhhAYGPouGGgXu02ymVwF6k+WmBc4ij0qynPRtXEE=" + [mod."nullprogram.com/x/optparse"] + version = "v1.0.0" + hash = "sha256-qfqfwuRikFqp1hjQRJ4MV0JdcUgswRlRe6i2XiH99/s=" + [mod."rsc.io/pdf"] + version = "v0.1.1" + hash = "sha256-BHVEebJPCm+e4MIyfp2IQCP2y8MdmNG+FKpYixSXEgc=" + [mod."sigs.k8s.io/json"] + version = "v0.0.0-20221116044647-bc3834ca7abd" + hash = "sha256-XDBMN2o450IHiAwEpBVsvo9e7tYZa+EXWrifUNTdNMU=" + [mod."sigs.k8s.io/structured-merge-diff/v4"] + version = "v4.4.1" + hash = "sha256-FcZHHZCKNNZW6/s1T1sKiS5Vj1TpHPmxVWr6YlL60xA=" + [mod."sigs.k8s.io/yaml"] + version = "v1.3.0" + hash = "sha256-RVp8vca2wxg8pcBDYospG7Z1dujoH7zXNu2rgZ1kky0=" diff --git a/kardinal-cli/main.go b/kardinal-cli/main.go new file mode 100644 index 00000000..c7978acd --- /dev/null +++ b/kardinal-cli/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "kardinal.cli/cmd" +) + +func main() { + if err := cmd.Execute(); err != nil { + println("Error:", err.Error()) + } +} diff --git a/kardinal-cli/shell.nix b/kardinal-cli/shell.nix new file mode 100644 index 00000000..894c97d9 --- /dev/null +++ b/kardinal-cli/shell.nix @@ -0,0 +1,17 @@ +{pkgs}: let + goEnv = pkgs.mkGoEnv {pwd = ./.;}; +in + pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + goEnv + + goreleaser + go + gopls + golangci-lint + delve + enumer + gomod2nix + bash-completion + ]; + } diff --git a/kardinal-manager/default.nix b/kardinal-manager/default.nix new file mode 100644 index 00000000..e625e82e --- /dev/null +++ b/kardinal-manager/default.nix @@ -0,0 +1,20 @@ +{ + pkgs, + commit_hash ? "dirty", +}: let + pname = "kardinal-manager"; + ldflags = pkgs.lib.concatStringsSep "\n" [ + "-X github.com/kurtosis-tech/kurtosis/kardinal.AppName=${pname}" + "-X github.com/kurtosis-tech/kurtosis/kardinal.Commit=${commit_hash}" + ]; +in + pkgs.buildGoApplication { + # pname has to match the location (folder) where the main function is or use + # subPackges to specify the file (e.g. subPackages = ["some/folder/main.go"];) + inherit pname ldflags; + name = "${pname}"; + pwd = ./.; + src = ./.; + modules = ./gomod2nix.toml; + CGO_ENABLED = 0; + } diff --git a/kardinal-manager/deployment/k8s.yaml b/kardinal-manager/deployment/k8s.yaml new file mode 100644 index 00000000..5f88d144 --- /dev/null +++ b/kardinal-manager/deployment/k8s.yaml @@ -0,0 +1,57 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kardinal-manager + namespace: default + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kardinal-manager-role +rules: + - apiGroups: ["*"] + resources: ["pods", "services", "deployments", "virtualservices", "workloadgroups", "workloadentries", "sidecars", "serviceentries", "gateways", "envoyfilters", "destinationrules"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kardinal-manager-binding +subjects: + - kind: ServiceAccount + name: kardinal-manager + namespace: default +roleRef: + kind: ClusterRole + name: kardinal-manager-role + apiGroup: rbac.authorization.k8s.io + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kardinal-manager + namespace: default +spec: + replicas: 1 + selector: + matchLabels: + app: kardinal-manager + template: + metadata: + labels: + app: kardinal-manager + spec: + serviceAccountName: kardinal-manager + containers: + - name: kardinal-manager + image: kurtosistech/kardinal-manager:latest + # TODO: Policy to local dev only - figure a way to remove it + imagePullPolicy: Never + env: + - name: KUBERNETES_SERVICE_HOST + value: "kubernetes.default.svc" + - name: KUBERNETES_SERVICE_PORT + value: "443" diff --git a/kardinal-manager/go.mod b/kardinal-manager/go.mod new file mode 100644 index 00000000..8aefdc52 --- /dev/null +++ b/kardinal-manager/go.mod @@ -0,0 +1,80 @@ +module kardinal.kontrol + +go 1.22.0 + +toolchain go1.22.3 + +replace github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api => ../libs/manager-kontrol-api + +require ( + github.com/deepmap/oapi-codegen/v2 v2.2.1-0.20240604070534-2f0ff757704b + github.com/getkin/kin-openapi v0.125.0 + github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api v0.0.0-00010101000000-000000000000 + github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409 + github.com/labstack/echo/v4 v4.12.0 + github.com/oapi-codegen/runtime v1.1.1 + github.com/samber/lo v1.39.0 + github.com/sirupsen/logrus v1.9.3 + github.com/stretchr/testify v1.9.0 + istio.io/api v1.22.1-0.20240524024004-b6815be0740d + istio.io/client-go v1.22.1 + k8s.io/api v0.30.2 + k8s.io/apimachinery v0.30.2 + k8s.io/client-go v0.29.0 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.8 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/moby/spdystream v0.2.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.16.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.21.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/kardinal-manager/go.sum b/kardinal-manager/go.sum new file mode 100644 index 00000000..f39d74b2 --- /dev/null +++ b/kardinal-manager/go.sum @@ -0,0 +1,238 @@ +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen/v2 v2.2.1-0.20240604070534-2f0ff757704b h1:nSyP/gj8okzyHlWoaqOEtNgqxSrrhCmyTtw1t9kFly8= +github.com/deepmap/oapi-codegen/v2 v2.2.1-0.20240604070534-2f0ff757704b/go.mod h1:L4zUv7ULYDtYSb/aYk/xO3OYcQU6BoU/0viULkbi2DE= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/getkin/kin-openapi v0.125.0 h1:jyQCyf2qXS1qvs2U00xQzkGCqYPhEhZDmSmVt65fXno= +github.com/getkin/kin-openapi v0.125.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409 h1:YQTATifMUwZEtZYb0LVA7DK2pj8s71iY8rzweuUQ5+g= +github.com/kurtosis-tech/stacktrace v0.0.0-20211028211901-1c67a77b5409/go.mod h1:y5weVs5d9wXXHcDA1awRxkIhhHC1xxYJN8a7aXnE6S8= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= +github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= +golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +istio.io/api v1.22.1-0.20240524024004-b6815be0740d h1:2GncSQ55NOr91NYPmi0jqhVM7z7/xswJsD96dQMkN38= +istio.io/api v1.22.1-0.20240524024004-b6815be0740d/go.mod h1:S3l8LWqNYS9yT+d4bH+jqzH2lMencPkW7SKM1Cu9EyM= +istio.io/client-go v1.22.1 h1:78BUMxytD0muwpwHdcA9qTOTJXN0jib0mXmNLdXxj0c= +istio.io/client-go v1.22.1/go.mod h1:Z2QE9uMt6tDVyrmiLfLVhutbqtfUkPJ7A5Uw/p6gNFo= +k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI= +k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI= +k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg= +k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= +k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/kardinal-manager/gomod2nix.toml b/kardinal-manager/gomod2nix.toml new file mode 100644 index 00000000..413de668 --- /dev/null +++ b/kardinal-manager/gomod2nix.toml @@ -0,0 +1,561 @@ +schema = 3 + +[mod] + [mod."cloud.google.com/go/compute"] + version = "v1.20.1" + hash = "sha256-HQvSLdoSagQsuqOb/D9+xMc8nE9Y44Z30rQgd2AGEr8=" + [mod."cloud.google.com/go/compute/metadata"] + version = "v0.2.3" + hash = "sha256-kYB1FTQRdTDqCqJzSU/jJYbVUGyxbkASUKbEs36FUyU=" + [mod."github.com/BurntSushi/toml"] + version = "v1.3.2" + hash = "sha256-FIwyH67KryRWI9Bk4R8s1zFP0IgKR4L66wNQJYQZLeg=" + [mod."github.com/CloudyKit/fastprinter"] + version = "v0.0.0-20200109182630-33d98a066a53" + hash = "sha256-KUuNS6OlaDUfpKIvZWJr8fiUR8ki/hUwMZiunNj0cxo=" + [mod."github.com/CloudyKit/jet/v6"] + version = "v6.2.0" + hash = "sha256-22PSPgN9ajVbm0gbhnrPWgroacipQaL7AMLsECzzd7A=" + [mod."github.com/Joker/jade"] + version = "v1.1.3" + hash = "sha256-264xyHGlF/hqJGY28YAFFh4VcsoJ0W/5HrgcJAdLKZs=" + [mod."github.com/NYTimes/gziphandler"] + version = "v0.0.0-20170623195520-56545f4a5d46" + hash = "sha256-4mTVrxEH1Cu3MVhm/nB+Zm8b2oYS4SecOHjnbT5Pk7s=" + [mod."github.com/RaveNoX/go-jsoncommentstrip"] + version = "v1.0.0" + hash = "sha256-bD0Jb4TGkJhuJ1MGptK+eTpGseRom1QRO9hwxnoF60U=" + [mod."github.com/Shopify/goreferrer"] + version = "v0.0.0-20220729165902-8cddb4f5de06" + hash = "sha256-zyP8NdtP79I7DOH7bUw54pY5ge1yWkSIbh4jnp8gel4=" + [mod."github.com/andybalholm/brotli"] + version = "v1.0.5" + hash = "sha256-/qS8wU8yZQJ+uTOg66rEl9s7spxq9VIXF5L1BcaEClc=" + [mod."github.com/apapsch/go-jsonmerge/v2"] + version = "v2.0.0" + hash = "sha256-xp/1B6XUN2EbddBfoUkTV3oTk+34m4kOZP+66HhfLg4=" + [mod."github.com/armon/go-socks5"] + version = "v0.0.0-20160902184237-e75332964ef5" + hash = "sha256-2F/mqTbr7jhtlZ0UGMRfHrjXbHbGwTJi951y4CQInIA=" + [mod."github.com/asaskevich/govalidator"] + version = "v0.0.0-20190424111038-f61b66f89f4a" + hash = "sha256-pcrINvdGpQExaSQv1j19L0NLWcvZL8jXQMsvMlVq8ss=" + [mod."github.com/aymerick/douceur"] + version = "v0.2.0" + hash = "sha256-NiBX8EfOvLXNiK3pJaZX4N73YgfzdrzRXdiBFe3X3sE=" + [mod."github.com/bmatcuk/doublestar"] + version = "v1.1.1" + hash = "sha256-mcR+gZ11pja98RwW7eN6T6TwC07ujPmQ8CcyH45r//0=" + [mod."github.com/bsm/ginkgo/v2"] + version = "v2.12.0" + hash = "sha256-rZ9AF2RPWwQcgFmh3bmUXDaTLL14fQfPtv+DuC8xqzQ=" + [mod."github.com/bsm/gomega"] + version = "v1.27.10" + hash = "sha256-EltjBddI8lJOxiONk2GgbjtHp7ysV00CK7BRVbAOLZ4=" + [mod."github.com/bytedance/sonic"] + version = "v1.11.6" + hash = "sha256-oGDdBbAHDwQYFFcg3AeYaHwOGQNa1q4n/0w4y2eqsxk=" + [mod."github.com/bytedance/sonic/loader"] + version = "v0.1.1" + hash = "sha256-9MzO8LYUrun40uXTexcbKtQO1MvXhlD6Er6T6v9TOtE=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.3.0" + hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY=" + [mod."github.com/chenzhuoyu/base64x"] + version = "v0.0.0-20230717121745-296ad89f973d" + hash = "sha256-o1qbpdkfbXE/DWW1ZFgTLPS2HGS0Ib69dbd6zO8CCWk=" + [mod."github.com/chenzhuoyu/iasm"] + version = "v0.9.0" + hash = "sha256-xlZIAcRAD9dufk7JZfyKyiBzw6Gzfj4oKh2wbjKukQg=" + [mod."github.com/cloudwego/base64x"] + version = "v0.1.4" + hash = "sha256-umCZR3iNmHFm+BC76kfpdcRG+pTQd6Jcu/c2kQDnyfw=" + [mod."github.com/cloudwego/iasm"] + version = "v0.2.0" + hash = "sha256-TzIP2N3HOesXrKACsRr/ShcoqttwPGZPckIepsTyHOA=" + [mod."github.com/compose-spec/compose-go"] + version = "v1.20.2" + hash = "sha256-uICczxxRYLAW4jMHEsNxcOUBoCT5l5AMSrexHJ3xTcA=" + [mod."github.com/cpuguy83/go-md2man/v2"] + version = "v2.0.3" + hash = "sha256-FAMxR5eBO9LQp6ev1b7zaPUS5aoNz1GtsPpoArjiJVw=" + [mod."github.com/creack/pty"] + version = "v1.1.9" + hash = "sha256-Rj6c4/3IApJcS36iPVIEdlMSC/SWmywnpqk1500ik5k=" + [mod."github.com/davecgh/go-spew"] + version = "v1.1.1" + hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI=" + [mod."github.com/deepmap/oapi-codegen/v2"] + version = "v2.2.1-0.20240604070534-2f0ff757704b" + hash = "sha256-xexjMUdpXoOS9i8dA3MD4mBFHcXSnk9y6iKZfeSXjHg=" + [mod."github.com/dgryski/go-rendezvous"] + version = "v0.0.0-20200823014737-9f7001d12a5f" + hash = "sha256-n/7xo5CQqo4yLaWMSzSN1Muk/oqK6O5dgDOFWapeDUI=" + [mod."github.com/distribution/reference"] + version = "v0.5.0" + hash = "sha256-nTlqurp/J/xOZeR0JcJMmE4GUHMMd5I+EOR7huzYuok=" + [mod."github.com/docker/go-connections"] + version = "v0.4.0" + hash = "sha256-GHNIjOuuNp5lFQ308+nDNwQPGESCVV7bCUxSW5ZxZlc=" + [mod."github.com/docker/go-units"] + version = "v0.5.0" + hash = "sha256-iK/V/jJc+borzqMeqLY+38Qcts2KhywpsTk95++hImE=" + [mod."github.com/emicklei/go-restful/v3"] + version = "v3.11.0" + hash = "sha256-Kp5ndPvj1PhK0nscM1pNNK2Q4ahUuLED/baEgL9pKNo=" + [mod."github.com/evanphx/json-patch"] + version = "v5.6.0+incompatible" + hash = "sha256-F4hI+H6oWFCKa47+x74M4fqcwDtv3wXOLGVxqDn0F5c=" + [mod."github.com/fatih/structs"] + version = "v1.1.0" + hash = "sha256-OCmubTLF1anwNnkvFZDYHnF6hFlX0WDoe/9+dDlaMPM=" + [mod."github.com/felixge/fgprof"] + version = "v0.9.3" + hash = "sha256-Q0EOEvkwqNbB/yR85MGrbzoahGbWbSw8ISmP0KTdAw8=" + [mod."github.com/flosch/pongo2/v4"] + version = "v4.0.2" + hash = "sha256-MQa2y64XpNPa3dKEerYJT5eFTrAk7flts9h2LG1QQQY=" + [mod."github.com/fxamacker/cbor/v2"] + version = "v2.6.0" + hash = "sha256-8EMjmc2FYVb0OXuzU1FWkSqYEtJjYdIT2PWsChoiTyQ=" + [mod."github.com/gabriel-vasile/mimetype"] + version = "v1.4.3" + hash = "sha256-EDmlRi3av27dq/ISVTglv08z4yZzMQ/SxL1c46EJro0=" + [mod."github.com/getkin/kin-openapi"] + version = "v0.125.0" + hash = "sha256-4g7MoGIyH80ek9SEqHYV1TjsYAx+wTzvipEKhfddGYA=" + [mod."github.com/gin-contrib/sse"] + version = "v0.1.0" + hash = "sha256-zYbMTao+1F+385Lvsba9roLmmt9eYqr57sUWo0LCVhw=" + [mod."github.com/gin-gonic/gin"] + version = "v1.10.0" + hash = "sha256-esJasHrJtuTBwGPGAoc/XSb428J8va+tPGcZ0gTfsgc=" + [mod."github.com/go-logr/logr"] + version = "v1.4.1" + hash = "sha256-WM4badoqxXlBmqCRrnmtNce63dLlr/FJav3BJSYHvaY=" + [mod."github.com/go-openapi/jsonpointer"] + version = "v0.20.2" + hash = "sha256-z9IZxP+JvJ1WvrHE7qbAZQqJ3XMx1uD0S611shTMna8=" + [mod."github.com/go-openapi/jsonreference"] + version = "v0.20.2" + hash = "sha256-klWZKK7LZqSg3HMIrSkjh/NwaZTr+8kTW2ok2+JlioE=" + [mod."github.com/go-openapi/swag"] + version = "v0.22.8" + hash = "sha256-YMJpCbWT9ABlCmuLjxQRwhddlYYqDBWcyDIdnV8bAMc=" + [mod."github.com/go-playground/assert/v2"] + version = "v2.2.0" + hash = "sha256-jBDvfGBS2EWzN6Ve+ZU2TyAj3c2Wqbxw88kz2NsBq44=" + [mod."github.com/go-playground/locales"] + version = "v0.14.1" + hash = "sha256-BMJGAexq96waZn60DJXZfByRHb8zA/JP/i6f/YrW9oQ=" + [mod."github.com/go-playground/universal-translator"] + version = "v0.18.1" + hash = "sha256-2/B2qP51zfiY+k8G0w0D03KXUc7XpWj6wKY7NjNP/9E=" + [mod."github.com/go-playground/validator/v10"] + version = "v10.20.0" + hash = "sha256-FKF+ebrSedNdh5Wq8aE8UP+5LiM8B28bk8v3gyqqdDk=" + [mod."github.com/go-task/slim-sprig"] + version = "v0.0.0-20230315185526-52ccab3ef572" + hash = "sha256-D6NjCQbcYC53NdwzyAm4i9M1OjTJIVu4EIt3AD/Vxfg=" + [mod."github.com/go-test/deep"] + version = "v1.0.8" + hash = "sha256-AKOpW32qcq1BTVEPC6MshiFA3ROucnmlJMIfvwYXvNY=" + [mod."github.com/goccy/go-json"] + version = "v0.10.2" + hash = "sha256-6fMD2/Rku8HT0zDdeA23pX0YxbohiIOC8OJNYbylJTQ=" + [mod."github.com/gogo/protobuf"] + version = "v1.3.2" + hash = "sha256-pogILFrrk+cAtb0ulqn9+gRZJ7sGnnLLdtqITvxvG6c=" + [mod."github.com/golang-jwt/jwt"] + version = "v3.2.2+incompatible" + hash = "sha256-LOkpuXhWrFayvVf1GOaOmZI5YKEsgqVSb22aF8LnCEM=" + [mod."github.com/golang/groupcache"] + version = "v0.0.0-20210331224755-41bb18bfe9da" + hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" + [mod."github.com/golang/protobuf"] + version = "v1.5.4" + hash = "sha256-N3+Lv9lEZjrdOWdQhFj6Y3Iap4rVLEQeI8/eFFyAMZ0=" + [mod."github.com/golang/snappy"] + version = "v0.0.4" + hash = "sha256-Umx+5xHAQCN/Gi4HbtMhnDCSPFAXSsjVbXd8n5LhjAA=" + [mod."github.com/gomarkdown/markdown"] + version = "v0.0.0-20230922112808-5421fefb8386" + hash = "sha256-N3qjdMq0L4toiTu2rut9sSPR/cA7bEe/bZ1iEVNzXOE=" + [mod."github.com/google/btree"] + version = "v1.0.1" + hash = "sha256-1PIeFGgUL4BK/StL/D12pg9bEQ5HfMT/fMLdus4pZTs=" + [mod."github.com/google/gnostic-models"] + version = "v0.6.8" + hash = "sha256-YzA/XpvPyfdplJtHmAUdQk9P+j0NBwHhW9nj1DaGaoQ=" + [mod."github.com/google/go-cmp"] + version = "v0.6.0" + hash = "sha256-qgra5jze4iPGP0JSTVeY5qV5AvEnEu39LYAuUCIkMtg=" + [mod."github.com/google/gofuzz"] + version = "v1.2.0" + hash = "sha256-T6Gz741l45L3F6Dt7fiAuQvQQg59Qtap3zG05M2cfqU=" + [mod."github.com/google/pprof"] + version = "v0.0.0-20211214055906-6f57359322fd" + hash = "sha256-Ju5Ke03QOM0YbubQT8jPPcjJq2mgpua8RmqB1UUSLAw=" + [mod."github.com/google/uuid"] + version = "v1.6.0" + hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw=" + [mod."github.com/gorilla/css"] + version = "v1.0.0" + hash = "sha256-Mmt/IqHpgrtWpbr/AKcJyf/USQTqEuv1HVivY4eHzoQ=" + [mod."github.com/gorilla/mux"] + version = "v1.8.1" + hash = "sha256-nDABvAhlYgxUW2N/brrep7NkQXoSGcHhA+XI4+tK0F0=" + [mod."github.com/gorilla/websocket"] + version = "v1.5.0" + hash = "sha256-EYVgkSEMo4HaVrsWKqnsYRp8SSS8gNf7t+Elva02Ofc=" + [mod."github.com/gregjones/httpcache"] + version = "v0.0.0-20180305231024-9cad4c3443a7" + hash = "sha256-2ngFfFuSm8YSTNZWGQuN5yTpsXlwY2R8aaIzjDnjTXI=" + [mod."github.com/imdario/mergo"] + version = "v0.3.16" + hash = "sha256-gh2TEAq8YrZOEAf6SFW4AIcEEUguD68G+7/VUnBeWwM=" + [mod."github.com/inconshreveable/mousetrap"] + version = "v1.1.0" + hash = "sha256-XWlYH0c8IcxAwQTnIi6WYqq44nOKUylSWxWO/vi+8pE=" + [mod."github.com/invopop/yaml"] + version = "v0.2.0" + hash = "sha256-RxeDuvwOSWYaLc8Q7T39rfFT3bZX3g9Bu0RFwxH6sLw=" + [mod."github.com/iris-contrib/schema"] + version = "v0.0.6" + hash = "sha256-2jk46kocpQGmswalwwFI5P8B4AHR1xkTnbPdtFa9VFo=" + [mod."github.com/josharian/intern"] + version = "v1.0.0" + hash = "sha256-LJR0QE2vOQ2/2shBbO5Yl8cVPq+NFrE3ers0vu9FRP0=" + [mod."github.com/json-iterator/go"] + version = "v1.1.12" + hash = "sha256-To8A0h+lbfZ/6zM+2PpRpY3+L6725OPC66lffq6fUoM=" + [mod."github.com/juju/gnuflag"] + version = "v0.0.0-20171113085948-2ce1bb71843d" + hash = "sha256-D66Gq0FC0k+86qOMZrpEsQ+6HzqkBibCdHXZsN1BfmY=" + [mod."github.com/kataras/blocks"] + version = "v0.0.7" + hash = "sha256-sXTcBIPuifzEgqe8m0Oe45A19egRGY1C+ZndaKWxxuY=" + [mod."github.com/kataras/golog"] + version = "v0.1.9" + hash = "sha256-o/l8xq0bNRtjBbnCTjiIg4K2SXP3w1a+df9nBVI3VWQ=" + [mod."github.com/kataras/iris/v12"] + version = "v12.2.6-0.20230908161203-24ba4e8933b9" + hash = "sha256-49LV2uv5+jKCANxIMTdDp7i++kWlp91oiFhnPvnVkQM=" + [mod."github.com/kataras/pio"] + version = "v0.0.12" + hash = "sha256-+DnDBEQde7iLVBE9XQmEhYpR6YTDeVplniQWrZH964w=" + [mod."github.com/kataras/sitemap"] + version = "v0.0.6" + hash = "sha256-NmAX6wCT1Z5n44O5TId4iovDUxH/ukgLKEGmchAMS8Q=" + [mod."github.com/kataras/tunnel"] + version = "v0.0.4" + hash = "sha256-yhCeH7eM8F+BxOy/y5MyAI0F0ghfLFUB/5aemIBZM54=" + [mod."github.com/kisielk/errcheck"] + version = "v1.5.0" + hash = "sha256-ZmocFXtg+Thdup+RqDYC/Td3+m1nS0FydZecfsWXIzI=" + [mod."github.com/kisielk/gotool"] + version = "v1.0.0" + hash = "sha256-lsdQkue8gFz754PGqczUnvGiCQq87SruQtdrDdQVTpE=" + [mod."github.com/klauspost/compress"] + version = "v1.16.7" + hash = "sha256-8miX/lnXyNLPSqhhn5BesLauaIAxETpQpWtr1cu2f+0=" + [mod."github.com/klauspost/cpuid/v2"] + version = "v2.2.7" + hash = "sha256-bjinp7b7qWk+DcZDDv1EedJxZqGxp2NWY+NYKBfE5xU=" + [mod."github.com/knz/go-libedit"] + version = "v1.10.1" + hash = "sha256-9QLTNZPo8qq+6JhvlTbETNqx1SFrv7l9YLH1kQEF+CE=" + [mod."github.com/kr/pretty"] + version = "v0.3.1" + hash = "sha256-DlER7XM+xiaLjvebcIPiB12oVNjyZHuJHoRGITzzpKU=" + [mod."github.com/kr/pty"] + version = "v1.1.1" + hash = "sha256-AVeS+ivwNzIrgWQaLtsmO2f2MYGpxIVqdac/EzaYI1Q=" + [mod."github.com/kr/text"] + version = "v0.2.0" + hash = "sha256-fadcWxZOORv44oak3jTxm6YcITcFxdGt4bpn869HxUE=" + [mod."github.com/kurtosis-tech/stacktrace"] + version = "v0.0.0-20211028211901-1c67a77b5409" + hash = "sha256-xm9l7tlCb0U25WJvByFikWxnAmOwTmOtlSDBdbyDMMY=" + [mod."github.com/labstack/echo/v4"] + version = "v4.12.0" + hash = "sha256-TPXJv/6C53bnmcEYxa9g5Mft8u/rLT96q64tQ9+RtKU=" + [mod."github.com/labstack/gommon"] + version = "v0.4.2" + hash = "sha256-395+BETDpv15L2lsCiEccwakXgEQxKHlYBAU0Ot3qhY=" + [mod."github.com/leodido/go-urn"] + version = "v1.4.0" + hash = "sha256-Q6kplWkY37Tzy6GOme3Wut40jFK4Izun+ij/BJvcEu0=" + [mod."github.com/mailgun/raymond/v2"] + version = "v2.0.48" + hash = "sha256-HC2vbTL9eCgk1m00h6Mg6W/pu6n/Y7Qr4MJuVQIX4v0=" + [mod."github.com/mailru/easyjson"] + version = "v0.7.7" + hash = "sha256-NVCz8MURpxgOjHXqxOZExqV4bnpHggpeAOyZDArjcy4=" + [mod."github.com/mattn/go-colorable"] + version = "v0.1.13" + hash = "sha256-qb3Qbo0CELGRIzvw7NVM1g/aayaz4Tguppk9MD2/OI8=" + [mod."github.com/mattn/go-isatty"] + version = "v0.0.20" + hash = "sha256-qhw9hWtU5wnyFyuMbKx+7RB8ckQaFQ8D+8GKPkN3HHQ=" + [mod."github.com/mattn/go-shellwords"] + version = "v1.0.12" + hash = "sha256-H7sLKwLzQmcrkEa4SpkHFSpkrpWmX7foOGiKswBUdhs=" + [mod."github.com/microcosm-cc/bluemonday"] + version = "v1.0.25" + hash = "sha256-/crG5s6cDrJ55nkDBwugLUpY7U+vQuHpCkKm7nnN8Zc=" + [mod."github.com/mitchellh/mapstructure"] + version = "v1.5.0" + hash = "sha256-ztVhGQXs67MF8UadVvG72G3ly0ypQW0IRDdOOkjYwoE=" + [mod."github.com/moby/spdystream"] + version = "v0.2.0" + hash = "sha256-Feme5UoWuBCvnLPKw1ozKkF3SM7PAjjPFQZ3TJhghR0=" + [mod."github.com/modern-go/concurrent"] + version = "v0.0.0-20180306012644-bacd9c7ef1dd" + hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo=" + [mod."github.com/modern-go/reflect2"] + version = "v1.0.2" + hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU=" + [mod."github.com/mohae/deepcopy"] + version = "v0.0.0-20170929034955-c48cc78d4826" + hash = "sha256-TQMmKqIYwVhmMVh4RYQkAui97Eyj7poLmcAuDcHXsEk=" + [mod."github.com/munnerz/goautoneg"] + version = "v0.0.0-20191010083416-a7dc8b61c822" + hash = "sha256-79URDDFenmGc9JZu+5AXHToMrtTREHb3BC84b/gym9Q=" + [mod."github.com/mxk/go-flowrate"] + version = "v0.0.0-20140419014527-cca7078d478f" + hash = "sha256-gRTfRfff/LRxC1SXXnQd2tV3UTcTx9qu90DJIVIaGn8=" + [mod."github.com/oapi-codegen/runtime"] + version = "v1.1.1" + hash = "sha256-GRvzpQngQPAy59KvvcwhjYqjx4gttIhOtQKjqfQcNvI=" + [mod."github.com/onsi/ginkgo/v2"] + version = "v2.15.0" + hash = "sha256-E+f2d0RZ6DSpdh4OasCbT1vGckXsziM3hFt4HkI5x1s=" + [mod."github.com/onsi/gomega"] + version = "v1.31.0" + hash = "sha256-a7Bmw1MpIgZNgWenBUEh/R/IpUFVUnDL4zaFznU0ILo=" + [mod."github.com/opencontainers/go-digest"] + version = "v1.0.0" + hash = "sha256-cfVDjHyWItmUGZ2dzQhCHgmOmou8v7N+itDkLZVkqkQ=" + [mod."github.com/pelletier/go-toml/v2"] + version = "v2.2.2" + hash = "sha256-ukxk1Cfm6cQW18g/aa19tLcUu5BnF7VmfAvrDHAOl6A=" + [mod."github.com/perimeterx/marshmallow"] + version = "v1.1.5" + hash = "sha256-fFWjg0FNohDSV0/wUjQ8fBq1g8h6yIqTrHkxqL2Tt0s=" + [mod."github.com/peterbourgon/diskv"] + version = "v2.0.1+incompatible" + hash = "sha256-K4mEVjH0eyxyYHQRxdbmgJT0AJrfucUwGB2BplRRt9c=" + [mod."github.com/pkg/diff"] + version = "v0.0.0-20210226163009-20ebb0f2a09e" + hash = "sha256-0aP4CtvBp9lmxoIvKq6mrOyQidLubH1bG92RD/n7bbw=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/pkg/profile"] + version = "v1.7.0" + hash = "sha256-wd8L8WiPoojo/oVUshgiJdM/k367LL1XO5BL5u1L2UU=" + [mod."github.com/pmezard/go-difflib"] + version = "v1.0.0" + hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA=" + [mod."github.com/redis/go-redis/v9"] + version = "v9.5.3" + hash = "sha256-XqJGBm6y6PPo5pxRsmDX4TGMp62V+4kDHBqOVNOZFjA=" + [mod."github.com/rogpeppe/go-internal"] + version = "v1.12.0" + hash = "sha256-qvDNCe3l84/LgrA8X4O15e1FeDcazyX91m9LmXGXX6M=" + [mod."github.com/russross/blackfriday/v2"] + version = "v2.1.0" + hash = "sha256-R+84l1si8az5yDqd5CYcFrTyNZ1eSYlpXKq6nFt4OTQ=" + [mod."github.com/samber/lo"] + version = "v1.39.0" + hash = "sha256-bFJXbzFpUQM2xoNrHrlzn0RJZujd21H00zGBgo/JEb0=" + [mod."github.com/schollz/closestmatch"] + version = "v2.1.0+incompatible" + hash = "sha256-SpWqGfqlMkZPQ6TSf7NTaYMbQllBaBgPM8oxTBOTn7w=" + [mod."github.com/sirupsen/logrus"] + version = "v1.9.3" + hash = "sha256-EnxsWdEUPYid+aZ9H4/iMTs1XMvCLbXZRDyvj89Ebms=" + [mod."github.com/spf13/cobra"] + version = "v1.8.0" + hash = "sha256-oAE+fEaRfZPE541IPWE0GMeBBYgH2DMhtZNxzp7DFlY=" + [mod."github.com/spf13/pflag"] + version = "v1.0.5" + hash = "sha256-w9LLYzxxP74WHT4ouBspH/iQZXjuAh2WQCHsuvyEjAw=" + [mod."github.com/spkg/bom"] + version = "v0.0.0-20160624110644-59b7046e48ad" + hash = "sha256-HifA8ur35a/T83Xdaw9vR9EP2SssigF2DG3h1+wJoGA=" + [mod."github.com/stretchr/objx"] + version = "v0.5.2" + hash = "sha256-VKYxrrFb1nkX6Wu3tE5DoP9+fCttwSl9pgLN6567nck=" + [mod."github.com/stretchr/testify"] + version = "v1.9.0" + hash = "sha256-uUp/On+1nK+lARkTVtb5RxlW15zxtw2kaAFuIASA+J0=" + [mod."github.com/tdewolff/minify/v2"] + version = "v2.12.9" + hash = "sha256-eSyTz73LOBXGAMFDJ1/CtmuMiRDzC4HkB+0xXtpvafk=" + [mod."github.com/tdewolff/parse/v2"] + version = "v2.6.8" + hash = "sha256-uimZjGFbVz/xiaDHPnbqrEmqeyQF+x5Gp3PfdCsD710=" + [mod."github.com/tidwall/btree"] + version = "v1.7.0" + hash = "sha256-bnr6c7a0nqo2HyGqxHk0kEZCEsjLYkPbAVY9WzaZ30o=" + [mod."github.com/tidwall/match"] + version = "v1.1.1" + hash = "sha256-M2klhPId3Q3T3VGkSbOkYl/2nLHnsG+yMbXkPkyrRdg=" + [mod."github.com/tidwall/redcon"] + version = "v1.6.2" + hash = "sha256-EhMOHoYAucg7ahRc9ALiEOluvJCAVd9nwy7BKfDt6bc=" + [mod."github.com/twitchyliquid64/golang-asm"] + version = "v0.15.1" + hash = "sha256-HLk6oUe7EoITrNvP0y8D6BtIgIcmDZYtb/xl/dufIoY=" + [mod."github.com/ugorji/go/codec"] + version = "v1.2.12" + hash = "sha256-sp1LJ93UK7mFwgZqG8jxCgTCPgKR74HNU6XxX0Jfjm0=" + [mod."github.com/valyala/bytebufferpool"] + version = "v1.0.0" + hash = "sha256-I9FPZ3kCNRB+o0dpMwBnwZ35Fj9+ThvITn8a3Jr8mAY=" + [mod."github.com/valyala/fasttemplate"] + version = "v1.2.2" + hash = "sha256-gp+lNXE8zjO+qJDM/YbS6V43HFsYP6PKn4ux1qa5lZ0=" + [mod."github.com/vmihailenco/msgpack/v5"] + version = "v5.3.5" + hash = "sha256-Uj5xRZbtxE8evniQPuBVkxkEdMa/H/pt9s84J16swm8=" + [mod."github.com/vmihailenco/tagparser/v2"] + version = "v2.0.0" + hash = "sha256-M9QyaKhSmmYwsJk7gkjtqu9PuiqZHSmTkous8VWkWY0=" + [mod."github.com/x448/float16"] + version = "v0.8.4" + hash = "sha256-VKzMTMS9pIB/cwe17xPftCSK9Mf4Y6EuBEJlB4by5mE=" + [mod."github.com/xeipuuv/gojsonpointer"] + version = "v0.0.0-20180127040702-4e3ac2762d5f" + hash = "sha256-OEW9nOR3EeMzvqvqpdAgowpZlbXPLWM0JT+5bwWOxo8=" + [mod."github.com/xeipuuv/gojsonreference"] + version = "v0.0.0-20180127040603-bd5ef7bd5415" + hash = "sha256-ZbXA+ASQrTgBQzasUKC9vznrOGpquYyWr+uwpm46fvU=" + [mod."github.com/xeipuuv/gojsonschema"] + version = "v1.2.0" + hash = "sha256-1ERBEvxj3pvHkMS2mvmvmYRi8jgAaTquo5hwjDLAEdc=" + [mod."github.com/yosssi/ace"] + version = "v0.0.5" + hash = "sha256-0HnNZUypGGoQxFX214/eF7RJ9KxYpb56Q5Rn2tryOLM=" + [mod."github.com/yuin/goldmark"] + version = "v1.4.13" + hash = "sha256-GVwFKZY6moIS6I0ZGuio/WtDif+lkZRfqWS6b4AAJyI=" + [mod."golang.org/x/arch"] + version = "v0.8.0" + hash = "sha256-zz9sbr+yT6eqjHVlVBfDZVmIkzOT6DZFpN3eaQT4Afw=" + [mod."golang.org/x/crypto"] + version = "v0.23.0" + hash = "sha256-6hZjb/OazWFBef0C/aH63l49YQnzCh2vpIduzyfSSG8=" + [mod."golang.org/x/exp"] + version = "v0.0.0-20230713183714-613f0c0eb8a1" + hash = "sha256-VLE9CCOYpTdyBWaQ1YxXpGOBS74wpIR3JJ+JVzNyEkQ=" + [mod."golang.org/x/mod"] + version = "v0.17.0" + hash = "sha256-CLaPeF6uTFuRDv4oHwOQE6MCMvrzkUjWN3NuyywZjKU=" + [mod."golang.org/x/net"] + version = "v0.25.0" + hash = "sha256-IjFfXLYNj27WLF7vpkZ6mfFXBnp+7QER3OQ0RgjxN54=" + [mod."golang.org/x/oauth2"] + version = "v0.16.0" + hash = "sha256-fJfS9dKaq82WaYSVWHMnxNLWH8+L4aip/C1AfJi4FFI=" + [mod."golang.org/x/sync"] + version = "v0.7.0" + hash = "sha256-2ETllEu2GDWoOd/yMkOkLC2hWBpKzbVZ8LhjLu0d2A8=" + [mod."golang.org/x/sys"] + version = "v0.20.0" + hash = "sha256-mowlaoG2k4n1c1rApWef5EMiXd3I77CsUi8jPh6pTYA=" + [mod."golang.org/x/telemetry"] + version = "v0.0.0-20240228155512-f48c80bd79b2" + hash = "sha256-31mlnHWpwrXwsKBXdvFVphcACQ/DQ7tv1AGBxL6Cs4U=" + [mod."golang.org/x/term"] + version = "v0.20.0" + hash = "sha256-kU+OVJbYktTIn4ZTAdomsOjL069Vj45sdroEMRKaRDI=" + [mod."golang.org/x/text"] + version = "v0.15.0" + hash = "sha256-pBnj0AEkfkvZf+3bN7h6epCD2kurw59clDP7yWvxKlk=" + [mod."golang.org/x/time"] + version = "v0.5.0" + hash = "sha256-W6RgwgdYTO3byIPOFxrP2IpAZdgaGowAaVfYby7AULU=" + [mod."golang.org/x/tools"] + version = "v0.21.0" + hash = "sha256-TU0gAxUX410AYc/nMxxZiaqXeORih1cXbKh3sxKufVg=" + [mod."golang.org/x/xerrors"] + version = "v0.0.0-20220907171357-04be3eba64a2" + hash = "sha256-6+zueutgefIYmgXinOflz8qGDDDj0Zhv+2OkGhBTKno=" + [mod."google.golang.org/appengine"] + version = "v1.6.8" + hash = "sha256-decMa0MiWfW/Bzr8QPPzzpeya0YWGHhZAt4Cr/bD1wQ=" + [mod."google.golang.org/genproto/googleapis/api"] + version = "v0.0.0-20240318140521-94a12d6c2237" + hash = "sha256-Tl2ABoESriYlPfeawE9xd5gPQYGzW4j/Il6gXEw33f0=" + [mod."google.golang.org/genproto/googleapis/rpc"] + version = "v0.0.0-20240314234333-6e1732d8331c" + hash = "sha256-P5SBku16dYnK4koUQxTeGwPxAAWH8rxbDm2pOzFLo/Q=" + [mod."google.golang.org/grpc"] + version = "v1.62.1" + hash = "sha256-1su6X0YT7MUflrTJijbq1CiisADZHudEx5sJq01TEaE=" + [mod."google.golang.org/protobuf"] + version = "v1.34.1" + hash = "sha256-qnHqY6KLZiZDbTVTN6uzF4jedxROYlPCYHoiv6XI0sc=" + [mod."gopkg.in/check.v1"] + version = "v1.0.0-20201130134442-10cb98267c6c" + hash = "sha256-VlIpM2r/OD+kkyItn6vW35dyc0rtkJufA93rjFyzncs=" + [mod."gopkg.in/inf.v0"] + version = "v0.9.1" + hash = "sha256-z84XlyeWLcoYOvWLxPkPFgLkpjyb2Y4pdeGMyySOZQI=" + [mod."gopkg.in/ini.v1"] + version = "v1.67.0" + hash = "sha256-V10ahGNGT+NLRdKUyRg1dos5RxLBXBk1xutcnquc/+4=" + [mod."gopkg.in/yaml.v2"] + version = "v2.4.0" + hash = "sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0=" + [mod."gopkg.in/yaml.v3"] + version = "v3.0.1" + hash = "sha256-FqL9TKYJ0XkNwJFnq9j0VvJ5ZUU1RvH/52h/f5bkYAU=" + [mod."gotest.tools/v3"] + version = "v3.4.0" + hash = "sha256-mohKBcuKT29Illk/Gm7Bu5E8QxhRYr6sEGPn/oux65k=" + [mod."istio.io/api"] + version = "v1.22.1-0.20240524024004-b6815be0740d" + hash = "sha256-crSFKGbjxvJRenCJVCTc4bgVl4//h3jDugNMjmUSQM4=" + [mod."istio.io/client-go"] + version = "v1.22.1" + hash = "sha256-yBen7y8xbvpgTzUYxEUOBqwkJGHfVjqxFUxKrxkHdls=" + [mod."k8s.io/api"] + version = "v0.30.2" + hash = "sha256-sq8jdb0wnAIhTO/hactsJ34mRuUgEuUNOrKaQasbHpQ=" + [mod."k8s.io/apimachinery"] + version = "v0.30.2" + hash = "sha256-EBWG9PlSfRwMed/u6oF8ME8n44/U5HGBCacH+Mns2UI=" + [mod."k8s.io/client-go"] + version = "v0.29.0" + hash = "sha256-GuY06sFi5YaQ4h925hCgdzfIMUjTcKxupKP6h7RWI78=" + [mod."k8s.io/gengo/v2"] + version = "v2.0.0-20240228010128-51d4e06bde70" + hash = "sha256-igAJQwUliynE2iDbBYc2HONd744+r7o3mQtFyYDYLb4=" + [mod."k8s.io/klog/v2"] + version = "v2.120.1" + hash = "sha256-PU3q5ODCHEO8z//zUKsCq2tky6ZNx4xFl3nHSPJpuW8=" + [mod."k8s.io/kube-openapi"] + version = "v0.0.0-20240228011516-70dd3763d340" + hash = "sha256-2BUbi2eNJFSTcmLyVYBYBe9Lhi0TVJoH2zVQlQ9eZIA=" + [mod."k8s.io/utils"] + version = "v0.0.0-20230726121419-3b25d923346b" + hash = "sha256-r8VhhAYGPouGGgXu02ymVwF6k+WmBc4ij0qynPRtXEE=" + [mod."nullprogram.com/x/optparse"] + version = "v1.0.0" + hash = "sha256-qfqfwuRikFqp1hjQRJ4MV0JdcUgswRlRe6i2XiH99/s=" + [mod."rsc.io/pdf"] + version = "v0.1.1" + hash = "sha256-BHVEebJPCm+e4MIyfp2IQCP2y8MdmNG+FKpYixSXEgc=" + [mod."sigs.k8s.io/json"] + version = "v0.0.0-20221116044647-bc3834ca7abd" + hash = "sha256-XDBMN2o450IHiAwEpBVsvo9e7tYZa+EXWrifUNTdNMU=" + [mod."sigs.k8s.io/structured-merge-diff/v4"] + version = "v4.4.1" + hash = "sha256-FcZHHZCKNNZW6/s1T1sKiS5Vj1TpHPmxVWr6YlL60xA=" + [mod."sigs.k8s.io/yaml"] + version = "v1.3.0" + hash = "sha256-RVp8vca2wxg8pcBDYospG7Z1dujoH7zXNu2rgZ1kky0=" diff --git a/kardinal-manager/kardinal-manager/api/http_rest/client/client.gen.go b/kardinal-manager/kardinal-manager/api/http_rest/client/client.gen.go new file mode 100644 index 00000000..e0c66a88 --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/client/client.gen.go @@ -0,0 +1,502 @@ +// Package kardinal_manager_rest_client provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.2.1-0.20240604070534-2f0ff757704b DO NOT EDIT. +package kardinal_manager_rest_client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + . "kardinal.kontrol/kardinal-manager/api/http_rest/types" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // DeleteVirtualServices request + DeleteVirtualServices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetVirtualServices request + GetVirtualServices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostVirtualServicesWithBody request with any body + PostVirtualServicesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostVirtualServices(ctx context.Context, body PostVirtualServicesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) DeleteVirtualServices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteVirtualServicesRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetVirtualServices(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetVirtualServicesRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostVirtualServicesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostVirtualServicesRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostVirtualServices(ctx context.Context, body PostVirtualServicesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostVirtualServicesRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewDeleteVirtualServicesRequest generates requests for DeleteVirtualServices +func NewDeleteVirtualServicesRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/virtual-services") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetVirtualServicesRequest generates requests for GetVirtualServices +func NewGetVirtualServicesRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/virtual-services") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostVirtualServicesRequest calls the generic PostVirtualServices builder with application/json body +func NewPostVirtualServicesRequest(server string, body PostVirtualServicesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostVirtualServicesRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostVirtualServicesRequestWithBody generates requests for PostVirtualServices with any type of body +func NewPostVirtualServicesRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/virtual-services") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // DeleteVirtualServicesWithResponse request + DeleteVirtualServicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*DeleteVirtualServicesResponse, error) + + // GetVirtualServicesWithResponse request + GetVirtualServicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetVirtualServicesResponse, error) + + // PostVirtualServicesWithBodyWithResponse request with any body + PostVirtualServicesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostVirtualServicesResponse, error) + + PostVirtualServicesWithResponse(ctx context.Context, body PostVirtualServicesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostVirtualServicesResponse, error) +} + +type DeleteVirtualServicesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *VirtualService + JSONDefault *NotOk +} + +// Status returns HTTPResponse.Status +func (r DeleteVirtualServicesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteVirtualServicesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetVirtualServicesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *map[string]VirtualService + JSONDefault *NotOk +} + +// Status returns HTTPResponse.Status +func (r GetVirtualServicesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetVirtualServicesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PostVirtualServicesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *VirtualService + JSONDefault *NotOk +} + +// Status returns HTTPResponse.Status +func (r PostVirtualServicesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostVirtualServicesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// DeleteVirtualServicesWithResponse request returning *DeleteVirtualServicesResponse +func (c *ClientWithResponses) DeleteVirtualServicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*DeleteVirtualServicesResponse, error) { + rsp, err := c.DeleteVirtualServices(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteVirtualServicesResponse(rsp) +} + +// GetVirtualServicesWithResponse request returning *GetVirtualServicesResponse +func (c *ClientWithResponses) GetVirtualServicesWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetVirtualServicesResponse, error) { + rsp, err := c.GetVirtualServices(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetVirtualServicesResponse(rsp) +} + +// PostVirtualServicesWithBodyWithResponse request with arbitrary body returning *PostVirtualServicesResponse +func (c *ClientWithResponses) PostVirtualServicesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostVirtualServicesResponse, error) { + rsp, err := c.PostVirtualServicesWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostVirtualServicesResponse(rsp) +} + +func (c *ClientWithResponses) PostVirtualServicesWithResponse(ctx context.Context, body PostVirtualServicesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostVirtualServicesResponse, error) { + rsp, err := c.PostVirtualServices(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostVirtualServicesResponse(rsp) +} + +// ParseDeleteVirtualServicesResponse parses an HTTP response from a DeleteVirtualServicesWithResponse call +func ParseDeleteVirtualServicesResponse(rsp *http.Response) (*DeleteVirtualServicesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteVirtualServicesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest VirtualService + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest NotOk + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParseGetVirtualServicesResponse parses an HTTP response from a GetVirtualServicesWithResponse call +func ParseGetVirtualServicesResponse(rsp *http.Response) (*GetVirtualServicesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetVirtualServicesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest map[string]VirtualService + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest NotOk + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} + +// ParsePostVirtualServicesResponse parses an HTTP response from a PostVirtualServicesWithResponse call +func ParsePostVirtualServicesResponse(rsp *http.Response) (*PostVirtualServicesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostVirtualServicesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest VirtualService + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest NotOk + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} diff --git a/kardinal-manager/kardinal-manager/api/http_rest/generate.go b/kardinal-manager/kardinal-manager/api/http_rest/generate.go new file mode 100644 index 00000000..935635a1 --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/generate.go @@ -0,0 +1,5 @@ +package http_rest + +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=./specs/types_cfg.yaml ./specs/kardinal_manager_api.yaml +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=./specs/server_cfg.yaml ./specs/kardinal_manager_api.yaml +//go:generate go run github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen --config=./specs/client_cfg.yaml ./specs/kardinal_manager_api.yaml diff --git a/kardinal-manager/kardinal-manager/api/http_rest/server/server.gen.go b/kardinal-manager/kardinal-manager/api/http_rest/server/server.gen.go new file mode 100644 index 00000000..5f7326fc --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/server/server.gen.go @@ -0,0 +1,376 @@ +// Package kardinal_manager_server_rest_server provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.2.1-0.20240604070534-2f0ff757704b DO NOT EDIT. +package kardinal_manager_server_rest_server + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + "github.com/labstack/echo/v4" + strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" + . "kardinal.kontrol/kardinal-manager/api/http_rest/types" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Delete virtual service + // (DELETE /virtual-services) + DeleteVirtualServices(ctx echo.Context) error + // List virtual services + // (GET /virtual-services) + GetVirtualServices(ctx echo.Context) error + // Create virtual service + // (POST /virtual-services) + PostVirtualServices(ctx echo.Context) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// DeleteVirtualServices converts echo context to params. +func (w *ServerInterfaceWrapper) DeleteVirtualServices(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.DeleteVirtualServices(ctx) + return err +} + +// GetVirtualServices converts echo context to params. +func (w *ServerInterfaceWrapper) GetVirtualServices(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetVirtualServices(ctx) + return err +} + +// PostVirtualServices converts echo context to params. +func (w *ServerInterfaceWrapper) PostVirtualServices(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostVirtualServices(ctx) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.DELETE(baseURL+"/virtual-services", wrapper.DeleteVirtualServices) + router.GET(baseURL+"/virtual-services", wrapper.GetVirtualServices) + router.POST(baseURL+"/virtual-services", wrapper.PostVirtualServices) + +} + +type NotOkJSONResponse ResponseInfo + +type DeleteVirtualServicesRequestObject struct { +} + +type DeleteVirtualServicesResponseObject interface { + VisitDeleteVirtualServicesResponse(w http.ResponseWriter) error +} + +type DeleteVirtualServices200JSONResponse VirtualService + +func (response DeleteVirtualServices200JSONResponse) VisitDeleteVirtualServicesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type DeleteVirtualServicesdefaultJSONResponse struct { + Body ResponseInfo + StatusCode int +} + +func (response DeleteVirtualServicesdefaultJSONResponse) VisitDeleteVirtualServicesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(response.StatusCode) + + return json.NewEncoder(w).Encode(response.Body) +} + +type GetVirtualServicesRequestObject struct { +} + +type GetVirtualServicesResponseObject interface { + VisitGetVirtualServicesResponse(w http.ResponseWriter) error +} + +type GetVirtualServices200JSONResponse map[string]VirtualService + +func (response GetVirtualServices200JSONResponse) VisitGetVirtualServicesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetVirtualServicesdefaultJSONResponse struct { + Body ResponseInfo + StatusCode int +} + +func (response GetVirtualServicesdefaultJSONResponse) VisitGetVirtualServicesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(response.StatusCode) + + return json.NewEncoder(w).Encode(response.Body) +} + +type PostVirtualServicesRequestObject struct { + Body *PostVirtualServicesJSONRequestBody +} + +type PostVirtualServicesResponseObject interface { + VisitPostVirtualServicesResponse(w http.ResponseWriter) error +} + +type PostVirtualServices200JSONResponse VirtualService + +func (response PostVirtualServices200JSONResponse) VisitPostVirtualServicesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostVirtualServicesdefaultJSONResponse struct { + Body ResponseInfo + StatusCode int +} + +func (response PostVirtualServicesdefaultJSONResponse) VisitPostVirtualServicesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(response.StatusCode) + + return json.NewEncoder(w).Encode(response.Body) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // Delete virtual service + // (DELETE /virtual-services) + DeleteVirtualServices(ctx context.Context, request DeleteVirtualServicesRequestObject) (DeleteVirtualServicesResponseObject, error) + // List virtual services + // (GET /virtual-services) + GetVirtualServices(ctx context.Context, request GetVirtualServicesRequestObject) (GetVirtualServicesResponseObject, error) + // Create virtual service + // (POST /virtual-services) + PostVirtualServices(ctx context.Context, request PostVirtualServicesRequestObject) (PostVirtualServicesResponseObject, error) +} + +type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc +type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// DeleteVirtualServices operation middleware +func (sh *strictHandler) DeleteVirtualServices(ctx echo.Context) error { + var request DeleteVirtualServicesRequestObject + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.DeleteVirtualServices(ctx.Request().Context(), request.(DeleteVirtualServicesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteVirtualServices") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(DeleteVirtualServicesResponseObject); ok { + return validResponse.VisitDeleteVirtualServicesResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// GetVirtualServices operation middleware +func (sh *strictHandler) GetVirtualServices(ctx echo.Context) error { + var request GetVirtualServicesRequestObject + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetVirtualServices(ctx.Request().Context(), request.(GetVirtualServicesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetVirtualServices") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(GetVirtualServicesResponseObject); ok { + return validResponse.VisitGetVirtualServicesResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostVirtualServices operation middleware +func (sh *strictHandler) PostVirtualServices(ctx echo.Context) error { + var request PostVirtualServicesRequestObject + + var body PostVirtualServicesJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostVirtualServices(ctx.Request().Context(), request.(PostVirtualServicesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostVirtualServices") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostVirtualServicesResponseObject); ok { + return validResponse.VisitPostVirtualServicesResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/9yUz07bQBDGX2U1rcTFiVO4+UYLRRFtQAmlh4rD1h47C/buMjMORZHfvVrbJORP1SLB", + "padsdsfffvPbmVlC6irvLFphSJZAyN5ZxvbPxMnFXVikzgpaCUvtfWlSLcbZ+JadDXuczrHSYfWeMIcE", + "3sVr1bg75XjaS49t7qBpmggy5JSMD1qQwDeLvzymgplCIkcQQvqPg/bG98kSPDmPJKbzmroMw2/uqNIC", + "CdTGytEhRCCPHiEBYwULJGgiqJBZF214f8hCxhbhrNv4t0yuQmxwSXhfG8IMkh+dwPqOqHN2s/Lhft5i", + "KuGqDZlkCWjrKiicTqcXU4hgPPl8ARF8P55OxpOzZxJrt9eGpNblDGlhUtylYnW1L80ty23UrsMQZnrY", + "m081PZ1d5XWpji/Hij2mJu9LQuWOlMxRnWvKjNWl+qqtLpDUs7qJlJEDVjVjpsQpXYsbFGiRtKBKS4NW", + "1Ozk/ICVtplipAXSgE2GqkUZgRgpg9GdS575gggWSNz5HQ0/DEeBl/NotTeQwNFwNDyCCLyWeYsqXnQs", + "B9zB5C7tEqUlGKi27scZJHDS7m/SZ4g22+dwNHq15tl66D3tM6vTFJlD+k8uoA3KdV3Kn/RXhuOu2due", + "q6tK0+MqTdWTUT2Z8AK64FA5O8xumggKlF1iZyivjEtnmQlHurzcqPmXYNxb82/P9Yth2abKf8PqHe/h", + "eul4L9j7Glk+uuzxTUtwPUWEamz+uwb4RKhf2gCtQju0QsT26FzNrKqfWV2oMnY9M09OryGCmkpIYC7i", + "OYnjh4eH4V0fMMxwET/9GfRCcZhrzU3zOwAA//+rRL0K0gcAAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/kardinal-manager/kardinal-manager/api/http_rest/specs/client_cfg.yaml b/kardinal-manager/kardinal-manager/api/http_rest/specs/client_cfg.yaml new file mode 100644 index 00000000..b4d3cbbf --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/specs/client_cfg.yaml @@ -0,0 +1,11 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/deepmap/oapi-codegen/HEAD/configuration-schema.json +package: kardinal_manager_rest_client +generate: + client: true +additional-imports: + - package: kardinal.kontrol/kardinal-manager/api/http_rest/types + alias: . +output: ./client/client.gen.go +output-options: + # to make sure that all types are generated + skip-prune: true diff --git a/kardinal-manager/kardinal-manager/api/http_rest/specs/kardinal_manager_api.yaml b/kardinal-manager/kardinal-manager/api/http_rest/specs/kardinal_manager_api.yaml new file mode 100644 index 00000000..fc1314f0 --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/specs/kardinal_manager_api.yaml @@ -0,0 +1,111 @@ +openapi: 3.0.3 + +info: + title: Kardinal Manager RESTful API + description: RESTful API specification for the Kardinal Manager application, it's used to auto-generate client SDK's and server-side code + version: 0.1.0 + +servers: + - url: https://www.kardinal.dev/kardinal-manager/api + description: Kardinal manager server in Kardinal DEV + +paths: + + /virtual-services: + get: + tags: + - virtual-services + summary: List virtual services + responses: + default: + $ref: "#/components/responses/NotOk" + "200": + description: Successful response + content: + application/json: + schema: + type: object + additionalProperties: + $ref: "#/components/schemas/VirtualService" + + post: + tags: + - virtual-services + summary: Create virtual service + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/VirtualService" + responses: + default: + $ref: "#/components/responses/NotOk" + "200": + description: Successful response + content: + application/json: + schema: + $ref: "#/components/schemas/VirtualService" + + delete: + tags: + - virtual-services + summary: Delete virtual service + responses: + default: + $ref: "#/components/responses/NotOk" + "200": + description: Successful response + content: + application/json: + schema: + $ref: "#/components/schemas/VirtualService" + + +# ========================================================================================================================= +# ========================================================================================================================= +# > > > > > > > > > > > > > > > > > > > > > > > > Data Models < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < +# ========================================================================================================================= +# ========================================================================================================================= + +components: + responses: + NotOk: + description: Unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseInfo" + required: true + + schemas: + ResponseType: + type: string + enum: + - ERROR + - INFO + - WARNING + + ResponseInfo: + type: object + properties: + type: + $ref: "#/components/schemas/ResponseType" + message: + type: string + code: + type: integer + format: uint32 + required: + - type + - message + - code + + VirtualService: + type: object + properties: + name: + type: string + required: + - name diff --git a/kardinal-manager/kardinal-manager/api/http_rest/specs/server_cfg.yaml b/kardinal-manager/kardinal-manager/api/http_rest/specs/server_cfg.yaml new file mode 100644 index 00000000..e121b778 --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/specs/server_cfg.yaml @@ -0,0 +1,13 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/deepmap/oapi-codegen/HEAD/configuration-schema.json +package: kardinal_manager_server_rest_server +generate: + embedded-spec: true + echo-server: true + strict-server: true +additional-imports: + - package: kardinal.kontrol/kardinal-manager/api/http_rest/types + alias: . +output: ./server/server.gen.go +output-options: + # to make sure that all types are generated + skip-prune: true diff --git a/kardinal-manager/kardinal-manager/api/http_rest/specs/types_cfg.yaml b/kardinal-manager/kardinal-manager/api/http_rest/specs/types_cfg.yaml new file mode 100644 index 00000000..ce14c39a --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/specs/types_cfg.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/deepmap/oapi-codegen/HEAD/configuration-schema.json +package: kardinal_manager_rest_types +generate: + models: true +output: ./types/types.gen.go +output-options: + # to make sure that all types are generated + skip-prune: true diff --git a/kardinal-manager/kardinal-manager/api/http_rest/types/types.gen.go b/kardinal-manager/kardinal-manager/api/http_rest/types/types.gen.go new file mode 100644 index 00000000..3da3e0bb --- /dev/null +++ b/kardinal-manager/kardinal-manager/api/http_rest/types/types.gen.go @@ -0,0 +1,32 @@ +// Package kardinal_manager_rest_types provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.2.1-0.20240604070534-2f0ff757704b DO NOT EDIT. +package kardinal_manager_rest_types + +// Defines values for ResponseType. +const ( + ERROR ResponseType = "ERROR" + INFO ResponseType = "INFO" + WARNING ResponseType = "WARNING" +) + +// ResponseInfo defines model for ResponseInfo. +type ResponseInfo struct { + Code uint32 `json:"code"` + Message string `json:"message"` + Type ResponseType `json:"type"` +} + +// ResponseType defines model for ResponseType. +type ResponseType string + +// VirtualService defines model for VirtualService. +type VirtualService struct { + Name string `json:"name"` +} + +// NotOk defines model for NotOk. +type NotOk = ResponseInfo + +// PostVirtualServicesJSONRequestBody defines body for PostVirtualServices for application/json ContentType. +type PostVirtualServicesJSONRequestBody = VirtualService diff --git a/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go new file mode 100644 index 00000000..55ac0557 --- /dev/null +++ b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager.go @@ -0,0 +1,509 @@ +package cluster_manager + +import ( + "context" + "github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api/api/golang/types" + "github.com/kurtosis-tech/stacktrace" + "github.com/samber/lo" + istio "istio.io/api/networking/v1alpha3" + "istio.io/client-go/pkg/apis/networking/v1alpha3" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "kardinal.kontrol/kardinal-manager/topology" +) + +const ( + listOptionsTimeoutSeconds int64 = 10 + fieldManager = "kardinal-manager" + deleteOptionsGracePeriodSeconds int64 = 0 + istioLabel = "istio-injection" + enabledIstioValue = "enabled" +) + +var ( + globalListOptions = metav1.ListOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: "", + APIVersion: "", + }, + LabelSelector: "", + FieldSelector: "", + Watch: false, + AllowWatchBookmarks: false, + ResourceVersion: "", + ResourceVersionMatch: "", + TimeoutSeconds: int64Ptr(listOptionsTimeoutSeconds), + Limit: 0, + Continue: "", + SendInitialEvents: nil, + } + + globalGetOptions = metav1.GetOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: "", + APIVersion: "", + }, + ResourceVersion: "", + } + + globalCreateOptions = metav1.CreateOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: "", + APIVersion: "", + }, + DryRun: nil, + // We need every object to have this field manager so that the Kurtosis objects can all seamlessly modify Kubernetes resources + FieldManager: fieldManager, + FieldValidation: "", + } + + globalUpdateOptions = metav1.UpdateOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: "", + APIVersion: "", + }, + DryRun: nil, + // We need every object to have this field manager so that the Kurtosis objects can all seamlessly modify Kubernetes resources + FieldManager: fieldManager, + FieldValidation: "", + } + + globalDeletePolicy = metav1.DeletePropagationForeground + + globalDeleteOptions = metav1.DeleteOptions{ + TypeMeta: metav1.TypeMeta{ + Kind: "", + APIVersion: "", + }, + GracePeriodSeconds: int64Ptr(deleteOptionsGracePeriodSeconds), + Preconditions: nil, + OrphanDependents: nil, + PropagationPolicy: &globalDeletePolicy, + DryRun: nil, + } +) + +type ClusterManager struct { + kubernetesClient *kubernetesClient + istioClient *istioClient +} + +func NewClusterManager(kubernetesClient *kubernetesClient, istioClient *istioClient) *ClusterManager { + return &ClusterManager{kubernetesClient: kubernetesClient, istioClient: istioClient} +} + +func (manager *ClusterManager) GetVirtualServices(ctx context.Context, namespace string) ([]*v1alpha3.VirtualService, error) { + virtServiceClient := manager.istioClient.clientSet.NetworkingV1alpha3().VirtualServices(namespace) + + virtualServiceList, err := virtServiceClient.List(ctx, globalListOptions) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred retrieving virtual services from IstIo client.") + } + return virtualServiceList.Items, nil +} + +func (manager *ClusterManager) GetVirtualService(ctx context.Context, namespace string, name string) (*v1alpha3.VirtualService, error) { + virtServiceClient := manager.istioClient.clientSet.NetworkingV1alpha3().VirtualServices(namespace) + + virtualService, err := virtServiceClient.Get(ctx, name, globalGetOptions) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred retrieving virtual service '%s' from IstIo client", name) + } + return virtualService, nil +} + +func (manager *ClusterManager) GetDestinationRules(ctx context.Context, namespace string) ([]*v1alpha3.DestinationRule, error) { + destRuleClient := manager.istioClient.clientSet.NetworkingV1alpha3().DestinationRules(namespace) + + destinationRules, err := destRuleClient.List(ctx, globalListOptions) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred retrieving destination rules.") + } + return destinationRules.Items, nil +} + +func (manager *ClusterManager) GetDestinationRule(ctx context.Context, namespace string, rule string) (*v1alpha3.DestinationRule, error) { + destRuleClient := manager.istioClient.clientSet.NetworkingV1alpha3().DestinationRules(namespace) + + destinationRule, err := destRuleClient.Get(ctx, rule, globalGetOptions) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred retrieving destination rule '%s' from IstIo client", rule) + } + return destinationRule, nil +} + +// how to expose API to configure ordering of routing rule? https://istio.io/latest/docs/concepts/traffic-management/#routing-rule-precedence +func (manager *ClusterManager) AddRoutingRule(ctx context.Context, namespace string, vsName string, routingRule *istio.HTTPRoute) error { + virtServiceClient := manager.istioClient.clientSet.NetworkingV1alpha3().VirtualServices(namespace) + + vs, err := virtServiceClient.Get(ctx, vsName, globalGetOptions) + if err != nil { + return stacktrace.Propagate(err, "An error occurred retrieving virtual service '%s'", vsName) + } + // always prepend routing rules due to routing rule precedence + vs.Spec.Http = append([]*istio.HTTPRoute{routingRule}, vs.Spec.Http...) + _, err = virtServiceClient.Update(ctx, vs, metav1.UpdateOptions{}) + if err != nil { + return stacktrace.Propagate(err, "An error occurred updating virtual service '%s' with routing rule: %v", vsName, routingRule) + } + return nil +} + +func (manager *ClusterManager) AddSubset(ctx context.Context, namespace string, drName string, subset *istio.Subset) error { + destRuleClient := manager.istioClient.clientSet.NetworkingV1alpha3().DestinationRules(namespace) + + dr, err := destRuleClient.Get(ctx, drName, globalGetOptions) + if err != nil { + return stacktrace.Propagate(err, "An error occurred retrieving destination rule '%s'", drName) + } + // if there already exists a subset for the same, just update it + shouldAddNewSubset := true + for _, s := range dr.Spec.Subsets { + if s.Name == subset.Name { + s = subset + shouldAddNewSubset = false + } + } + if shouldAddNewSubset { + dr.Spec.Subsets = append(dr.Spec.Subsets, subset) + } + _, err = destRuleClient.Update(ctx, dr, metav1.UpdateOptions{}) + if err != nil { + return stacktrace.Propagate(err, "An error occurred updating destination rule '%s' with subset: %v", drName, subset) + } + return nil +} + +func (manager *ClusterManager) GetTopologyForNameSpace(namespace string) (map[string]*topology.Node, error) { + return manager.istioClient.topologyManager.FetchTopology(namespace) +} + +func (manager *ClusterManager) ApplyClusterResources(ctx context.Context, clusterResources *types.ClusterResources) error { + + allNSs := [][]string{ + lo.Uniq(lo.Map(*clusterResources.Services, func(item corev1.Service, _ int) string { return item.Namespace })), + lo.Uniq(lo.Map(*clusterResources.Deployments, func(item appsv1.Deployment, _ int) string { return item.Namespace })), + lo.Uniq(lo.Map(*clusterResources.VirtualServices, func(item v1alpha3.VirtualService, _ int) string { return item.Namespace })), + lo.Uniq(lo.Map(*clusterResources.DestinationRules, func(item v1alpha3.DestinationRule, _ int) string { return item.Namespace })), + {clusterResources.Gateway.Namespace}, + } + + uniqueNamespaces := lo.Uniq(lo.Flatten(allNSs)) + + var ensureNamespacesErr error + + for _, namespace := range uniqueNamespaces { + if err := manager.ensureNamespace(ctx, namespace); err != nil { + return stacktrace.Propagate(ensureNamespacesErr, "An error occurred while creating or updating cluster namespace '%s'", namespace) + } + } + + for _, service := range *clusterResources.Services { + if err := manager.createOrUpdateService(ctx, &service); err != nil { + return stacktrace.Propagate(err, "An error occurred while creating or updating service '%s'", service.GetName()) + } + } + + for _, deployment := range *clusterResources.Deployments { + if err := manager.createOrUpdateDeployment(ctx, &deployment); err != nil { + return stacktrace.Propagate(err, "An error occurred while creating or updating deployment '%s'", deployment.GetName()) + } + } + + for _, virtualService := range *clusterResources.VirtualServices { + if err := manager.createOrUpdateVirtualService(ctx, &virtualService); err != nil { + return stacktrace.Propagate(err, "An error occurred while creating or updating virtual service '%s'", virtualService.GetName()) + } + } + + for _, destinationRule := range *clusterResources.DestinationRules { + if err := manager.createOrUpdateDestinationRule(ctx, &destinationRule); err != nil { + return stacktrace.Propagate(err, "An error occurred while creating or updating destination rule '%s'", destinationRule.GetName()) + } + } + + if err := manager.createOrUpdateGateway(ctx, clusterResources.Gateway); err != nil { + return stacktrace.Propagate(err, "An error occurred while creating or updating the cluster gateway") + } + + return nil +} + +func (manager *ClusterManager) CleanUpClusterResources(ctx context.Context, clusterResources *types.ClusterResources) error { + + // Clean up services + servicesByNS := lo.GroupBy(*clusterResources.Services, func(item corev1.Service) string { + return item.Namespace + }) + for namespace, services := range servicesByNS { + if err := manager.cleanUpServicesInNamespace(ctx, namespace, services); err != nil { + return stacktrace.Propagate(err, "An error occurred cleaning up services '%+v' in namespace '%s'", services, namespace) + } + } + + // Clean up deployments + deploymentsByNS := lo.GroupBy(*clusterResources.Deployments, func(item appsv1.Deployment) string { return item.Namespace }) + for namespace, deployments := range deploymentsByNS { + if err := manager.cleanUpDeploymentsInNamespace(ctx, namespace, deployments); err != nil { + return stacktrace.Propagate(err, "An error occurred cleaning up deployments '%+v' in namespace '%s'", deployments, namespace) + } + } + + // Clean up virtual services + virtualServicesByNS := lo.GroupBy(*clusterResources.VirtualServices, func(item v1alpha3.VirtualService) string { return item.Namespace }) + for namespace, virtualServices := range virtualServicesByNS { + if err := manager.cleanUpVirtualServicesInNamespace(ctx, namespace, virtualServices); err != nil { + return stacktrace.Propagate(err, "An error occurred cleaning up virtual services '%+v' in namespace '%s'", virtualServices, namespace) + } + } + + // Clean up destination rules + destinationRulesByNS := lo.GroupBy(*clusterResources.DestinationRules, func(item v1alpha3.DestinationRule) string { + return item.Namespace + }) + for namespace, destinationRules := range destinationRulesByNS { + if err := manager.cleanUpDestinationRulesInNamespace(ctx, namespace, destinationRules); err != nil { + return stacktrace.Propagate(err, "An error occurred cleaning up destination rules '%+v' in namespace '%s'", destinationRules, namespace) + } + } + + // Clean up gateway + gatewaysByNs := map[string][]v1alpha3.Gateway{ + clusterResources.Gateway.GetNamespace(): {*clusterResources.Gateway}, + } + for namespace, gateways := range gatewaysByNs { + if err := manager.cleanUpGatewaysInNamespace(ctx, namespace, gateways); err != nil { + return stacktrace.Propagate(err, "An error occurred cleaning up gateways '%+v' in namespace '%s'", gateways, namespace) + } + } + + return nil +} + +func (manager *ClusterManager) ensureNamespace(ctx context.Context, name string) error { + + existingNamespace, err := manager.kubernetesClient.clientSet.CoreV1().Namespaces().Get(ctx, name, metav1.GetOptions{}) + if err == nil && existingNamespace != nil { + value, found := existingNamespace.Labels[istioLabel] + if !found || value != enabledIstioValue { + existingNamespace.Labels[istioLabel] = enabledIstioValue + manager.kubernetesClient.clientSet.CoreV1().Namespaces().Update(ctx, existingNamespace, globalUpdateOptions) + } + } else { + newNamespace := corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{ + istioLabel: enabledIstioValue, + }, + }, + } + _, err = manager.kubernetesClient.clientSet.CoreV1().Namespaces().Create(ctx, &newNamespace, globalCreateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to create Namespace: %s", name) + } + } + + return nil +} + +func (manager *ClusterManager) createOrUpdateService(ctx context.Context, service *corev1.Service) error { + serviceClient := manager.kubernetesClient.clientSet.CoreV1().Services(service.Namespace) + existingService, err := serviceClient.Get(ctx, service.Name, metav1.GetOptions{}) + if err != nil { + // Resource does not exist, create new one + _, err = serviceClient.Create(ctx, service, globalCreateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to create service: %s", service.GetName()) + } + } else { + // Update the resource version to the latest before updating + service.ResourceVersion = existingService.ResourceVersion + _, err = serviceClient.Update(ctx, service, globalUpdateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to update service: %s", service.GetName()) + } + } + + return nil +} + +func (manager *ClusterManager) createOrUpdateDeployment(ctx context.Context, deployment *appsv1.Deployment) error { + deploymentClient := manager.kubernetesClient.clientSet.AppsV1().Deployments(deployment.Namespace) + existingDeployment, err := deploymentClient.Get(ctx, deployment.Name, metav1.GetOptions{}) + if err != nil { + _, err = deploymentClient.Create(ctx, deployment, globalCreateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to create deployment: %s", deployment.GetName()) + } + } else { + deployment.ResourceVersion = existingDeployment.ResourceVersion + _, err = deploymentClient.Update(ctx, deployment, globalUpdateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to update deployment: %s", deployment.GetName()) + } + } + + return nil +} + +func (manager *ClusterManager) createOrUpdateVirtualService(ctx context.Context, virtualService *v1alpha3.VirtualService) error { + + virtServiceClient := manager.istioClient.clientSet.NetworkingV1alpha3().VirtualServices(virtualService.GetNamespace()) + + existingVirtService, err := virtServiceClient.Get(ctx, virtualService.Name, metav1.GetOptions{}) + if err != nil { + _, err = virtServiceClient.Create(ctx, virtualService, globalCreateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to create virtual service: %s", virtualService.GetName()) + } + } else { + virtualService.ResourceVersion = existingVirtService.ResourceVersion + _, err = virtServiceClient.Update(ctx, virtualService, globalUpdateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to update virtual service: %s", virtualService.GetName()) + } + } + + return nil +} + +func (manager *ClusterManager) createOrUpdateDestinationRule(ctx context.Context, destinationRule *v1alpha3.DestinationRule) error { + + destRuleClient := manager.istioClient.clientSet.NetworkingV1alpha3().DestinationRules(destinationRule.GetNamespace()) + + existingDestRule, err := destRuleClient.Get(ctx, destinationRule.Name, metav1.GetOptions{}) + if err != nil { + _, err = destRuleClient.Create(ctx, destinationRule, globalCreateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to create destination rule: %s", destinationRule.GetName()) + } + } else { + destinationRule.ResourceVersion = existingDestRule.ResourceVersion + _, err = destRuleClient.Update(ctx, destinationRule, globalUpdateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to update destination rule: %s", destinationRule.GetName()) + } + } + + return nil +} + +func (manager *ClusterManager) createOrUpdateGateway(ctx context.Context, gateway *v1alpha3.Gateway) error { + + gatewayClient := manager.istioClient.clientSet.NetworkingV1alpha3().Gateways(gateway.GetNamespace()) + existingGateway, err := gatewayClient.Get(ctx, gateway.Name, metav1.GetOptions{}) + if err != nil { + _, err = gatewayClient.Create(ctx, gateway, globalCreateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to create gateway: %s", gateway.GetName()) + } + } else { + gateway.ResourceVersion = existingGateway.ResourceVersion + _, err = gatewayClient.Update(ctx, gateway, globalUpdateOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to update gateway: %s", gateway.GetName()) + } + } + + return nil +} + +func (manager *ClusterManager) cleanUpServicesInNamespace(ctx context.Context, namespace string, servicesToKeep []corev1.Service) error { + serviceClient := manager.kubernetesClient.clientSet.CoreV1().Services(namespace) + allServices, err := serviceClient.List(ctx, globalListOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to list services in namespace %s", namespace) + } + for _, service := range allServices.Items { + _, exists := lo.Find(servicesToKeep, func(item corev1.Service) bool { return item.Name == service.Name }) + if !exists { + err = serviceClient.Delete(ctx, service.Name, globalDeleteOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to delete service %s", service.GetName()) + } + } + } + return nil +} + +func (manager *ClusterManager) cleanUpDeploymentsInNamespace(ctx context.Context, namespace string, deploymentsToKeep []appsv1.Deployment) error { + deploymentClient := manager.kubernetesClient.clientSet.AppsV1().Deployments(namespace) + allDeployments, err := deploymentClient.List(ctx, globalListOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to list deployments in namespace %s", namespace) + } + for _, deployment := range allDeployments.Items { + _, exists := lo.Find(deploymentsToKeep, func(item appsv1.Deployment) bool { return item.Name == deployment.Name }) + if !exists { + err = deploymentClient.Delete(ctx, deployment.Name, globalDeleteOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to delete deployment %s", deployment.GetName()) + } + } + } + return nil +} + +func (manager *ClusterManager) cleanUpVirtualServicesInNamespace(ctx context.Context, namespace string, virtualServicesToKeep []v1alpha3.VirtualService) error { + + virtServiceClient := manager.istioClient.clientSet.NetworkingV1alpha3().VirtualServices(namespace) + allVirtServices, err := virtServiceClient.List(ctx, globalListOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to list virtual services in namespace %s", namespace) + } + for _, virtService := range allVirtServices.Items { + _, exists := lo.Find(virtualServicesToKeep, func(item v1alpha3.VirtualService) bool { return item.Name == virtService.Name }) + if !exists { + err = virtServiceClient.Delete(ctx, virtService.Name, globalDeleteOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to delete virtual service %s", virtService.GetName()) + } + } + } + + return nil +} + +func (manager *ClusterManager) cleanUpDestinationRulesInNamespace(ctx context.Context, namespace string, destinationRulesToKeep []v1alpha3.DestinationRule) error { + + destRuleClient := manager.istioClient.clientSet.NetworkingV1alpha3().DestinationRules(namespace) + allDestRules, err := destRuleClient.List(ctx, globalListOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to list destination rules in namespace %s", namespace) + } + for _, destRule := range allDestRules.Items { + _, exists := lo.Find(destinationRulesToKeep, func(item v1alpha3.DestinationRule) bool { return item.Name == destRule.Name }) + if !exists { + err = destRuleClient.Delete(ctx, destRule.Name, globalDeleteOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to delete destination rule %s", destRule.GetName()) + } + } + } + + return nil +} + +func (manager *ClusterManager) cleanUpGatewaysInNamespace(ctx context.Context, namespace string, gatewaysToKeep []v1alpha3.Gateway) error { + + gatewayClient := manager.istioClient.clientSet.NetworkingV1alpha3().Gateways(namespace) + allGateways, err := gatewayClient.List(ctx, globalListOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to list gateways in namespace %s", namespace) + } + for _, gateway := range allGateways.Items { + _, exists := lo.Find(gatewaysToKeep, func(item v1alpha3.Gateway) bool { return item.Name == gateway.Name }) + if !exists { + err = gatewayClient.Delete(ctx, gateway.Name, globalDeleteOptions) + if err != nil { + return stacktrace.Propagate(err, "Failed to delete gateway %s", gateway.GetName()) + } + } + } + + return nil +} + +func int64Ptr(i int64) *int64 { return &i } diff --git a/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager_factory.go b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager_factory.go new file mode 100644 index 00000000..546f8b50 --- /dev/null +++ b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager_factory.go @@ -0,0 +1,73 @@ +package cluster_manager + +import ( + "github.com/kurtosis-tech/stacktrace" + "istio.io/client-go/pkg/clientset/versioned" + "k8s.io/client-go/discovery/cached/memory" + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + "kardinal.kontrol/kardinal-manager/topology" + "path/filepath" +) + +func CreateClusterManager() (*ClusterManager, error) { + kubernetesClientObj, err := createKubernetesClient() + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred while creating the Kubernetes client") + } + + istioClientObj, err := createIstioClient(kubernetesClientObj.config) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred while creating the Istio client") + } + + return NewClusterManager(kubernetesClientObj, istioClientObj), nil +} + +func createKubernetesClient() (*kubernetesClient, error) { + var config *rest.Config + + // Load in-cluster configuration + config, err := rest.InClusterConfig() + if err != nil { + // Fallback to out-of-cluster configuration (for local development) + home := homedir.HomeDir() + kubeConfig := filepath.Join(home, ".kube", "config") + config, err = clientcmd.BuildConfigFromFlags("", kubeConfig) + if err != nil { + return nil, stacktrace.Propagate(err, "impossible to get kubernetes client config either inside or outside the cluster") + } + } + + clientSet, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred while creating kubernetes client using config '%+v'", config) + } + + dynamicClient, err := dynamic.NewForConfig(config) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred while creating kubernetes dynamic client using config '%+v'", config) + } + + discoveryClient := memory.NewMemCacheClient(clientSet.Discovery()) + discoveryMapper := restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient) + + kubernetesClientObj := newKubernetesClient(config, clientSet, dynamicClient, discoveryMapper) + + return kubernetesClientObj, nil +} + +func createIstioClient(k8sConfig *rest.Config) (*istioClient, error) { + ic, err := versioned.NewForConfig(k8sConfig) + if err != nil { + return nil, stacktrace.Propagate(err, "An error occurred creating IstIo client from k8s config: %v", k8sConfig) + } + + istioClientObj := newIstioClient(ic, topology.NewTopologyManager(k8sConfig)) + + return istioClientObj, nil +} diff --git a/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager_test.go b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager_test.go new file mode 100644 index 00000000..30777f38 --- /dev/null +++ b/kardinal-manager/kardinal-manager/cluster_manager/cluster_manager_test.go @@ -0,0 +1,101 @@ +package cluster_manager + +import ( + "context" + "github.com/stretchr/testify/require" + istio "istio.io/api/networking/v1alpha3" + "testing" +) + +const ( + defaultNamespace = "default" +) + +func TestClusterManager_GetVirtualServices(t *testing.T) { + ctx := context.Background() + clusterManager, err := getClusterManagerForTesting(t) + require.NoError(t, err) + + virtualServices, err := clusterManager.GetVirtualServices(ctx, defaultNamespace) + require.NoError(t, err) + require.NotEmpty(t, virtualServices) +} + +func TestClusterManager_GetTopologyForNameSpace(t *testing.T) { + clusterManager, err := getClusterManagerForTesting(t) + require.NoError(t, err) + + graph, err := clusterManager.GetTopologyForNameSpace("ms-demo") + require.Empty(t, err) + require.NotNil(t, graph) +} + +// This test is to demonstrate using the ClusterManager to accomplish certain workflows +// assumes +// - default k8s namespace contains the services from the sample bookinfo application: https://istio.io/latest/docs/examples/bookinfo/, run +// - a destination rule for reviews service has been preconfigured with one version of reviews +// - a virtual service for reviews service has been preconfigured with one routing rule +func TestClusterManagerWorkflows(t *testing.T) { + ctx := context.Background() + clusterManager, err := getClusterManagerForTesting(t) + require.NoError(t, err) + + // verify that there exists a destination rule for the "reviews" service that only sends traffic to v1 + reviewsDestinationRule, err := clusterManager.GetDestinationRule(ctx, defaultNamespace, "reviews") + require.NoError(t, err) + require.NotEmpty(t, reviewsDestinationRule) + require.NotEmpty(t, reviewsDestinationRule.Spec.Subsets) + require.Equal(t, reviewsDestinationRule.Spec.Subsets[0].Name, "v1") + + // verify that there exists a virtual service for the "reviews" service + reviewsVirtualService, err := clusterManager.GetVirtualService(ctx, defaultNamespace, "reviews") + require.NoError(t, err) + require.NotEmpty(t, reviewsVirtualService) + require.NotEmpty(t, reviewsVirtualService.Spec.Http) + // TODO: may want to implement types in house to manage some of this stuff but for now jus use objects directly + require.Equal(t, reviewsVirtualService.Spec.Http[0].Route[0].Destination.Host, "reviews") + require.Equal(t, reviewsVirtualService.Spec.Http[0].Route[0].Destination.Subset, "v1") + + // register a new version of the reviews service + v2subset := &istio.Subset{ + Name: "v2", + Labels: map[string]string{ + "version": "v2", + }, + TrafficPolicy: nil, + } + err = clusterManager.AddSubset(ctx, defaultNamespace, "reviews", v2subset) + require.NoError(t, err) + + // add a routing rule that splits traffic between v1 and v2 + splitTraffic5050Rule := &istio.HTTPRoute{ + Route: []*istio.HTTPRouteDestination{ + { + Destination: &istio.Destination{ + Host: "reviews", + Subset: "v1", + Port: nil, + }, + Weight: 50, + }, + { + Destination: &istio.Destination{ + Host: "reviews", + Subset: "v2", + Port: nil, + }, + Weight: 50, + }, + }, + } + // can consider adjusting the AddRoutingRule api to only take in params we care about to make the api easier to use but again for now, KISS till we know more about use cases + err = clusterManager.AddRoutingRule(ctx, defaultNamespace, "reviews", splitTraffic5050Rule) +} + +// Note: test will only work if kubeconfig is available locally and a cluster is running +// these code is meant for local iteration for now and less for unit testing +func getClusterManagerForTesting(t *testing.T) (*ClusterManager, error) { + clusterManager, err := CreateClusterManager() + require.NoError(t, err) + return clusterManager, nil +} diff --git a/kardinal-manager/kardinal-manager/cluster_manager/istio_client.go b/kardinal-manager/kardinal-manager/cluster_manager/istio_client.go new file mode 100644 index 00000000..3323cd16 --- /dev/null +++ b/kardinal-manager/kardinal-manager/cluster_manager/istio_client.go @@ -0,0 +1,40 @@ +package cluster_manager + +import ( + "istio.io/client-go/pkg/clientset/versioned" + "kardinal.kontrol/kardinal-manager/topology" +) + +// IstIO ontology: +// - virtual services +// - host +// - routing rules +// - destination rules +// +// - destination rules +// - host +// - traffic policy +// - subsets +// +// TODO: implement this ontology later +// - gateways +// - service entries + +// use cases IstIo manager needs to support: +// - ability to configure traffic routing rules for services in a cluster +// - change the distribution of traffic to a service +// - redirect which service traffic is going to +// - duplicate traffic to services +// +// - ability to add new versions of a service +// - updating destination rules + +type istioClient struct { + clientSet *versioned.Clientset + + topologyManager *topology.Manager +} + +func newIstioClient(clientSet *versioned.Clientset, topologyManager *topology.Manager) *istioClient { + return &istioClient{clientSet: clientSet, topologyManager: topologyManager} +} diff --git a/kardinal-manager/kardinal-manager/cluster_manager/kubernetes_client.go b/kardinal-manager/kardinal-manager/cluster_manager/kubernetes_client.go new file mode 100644 index 00000000..e9945b5d --- /dev/null +++ b/kardinal-manager/kardinal-manager/cluster_manager/kubernetes_client.go @@ -0,0 +1,19 @@ +package cluster_manager + +import ( + "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/restmapper" +) + +type kubernetesClient struct { + config *rest.Config + clientSet *kubernetes.Clientset + dynamicClient *dynamic.DynamicClient + discoveryMapper *restmapper.DeferredDiscoveryRESTMapper +} + +func newKubernetesClient(config *rest.Config, clientSet *kubernetes.Clientset, dynamicClient *dynamic.DynamicClient, discoveryMapper *restmapper.DeferredDiscoveryRESTMapper) *kubernetesClient { + return &kubernetesClient{config: config, clientSet: clientSet, dynamicClient: dynamicClient, discoveryMapper: discoveryMapper} +} diff --git a/kardinal-manager/kardinal-manager/cluster_manager/test-examples/vs-example.yaml b/kardinal-manager/kardinal-manager/cluster_manager/test-examples/vs-example.yaml new file mode 100644 index 00000000..57f2a72a --- /dev/null +++ b/kardinal-manager/kardinal-manager/cluster_manager/test-examples/vs-example.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: reviews +spec: + hosts: + - reviews + http: +# - match: +# - uri: +# prefix: "/reviews/0" +# route: +# - destination: +# host: reviews +# subset: v2 + - route: + - destination: + host: reviews + subset: v1 \ No newline at end of file diff --git a/kardinal-manager/kardinal-manager/fetcher/fetcher.go b/kardinal-manager/kardinal-manager/fetcher/fetcher.go new file mode 100644 index 00000000..4c341224 --- /dev/null +++ b/kardinal-manager/kardinal-manager/fetcher/fetcher.go @@ -0,0 +1,93 @@ +package fetcher + +import ( + "context" + "encoding/json" + "github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api/api/golang/types" + "github.com/kurtosis-tech/stacktrace" + "github.com/sirupsen/logrus" + "io" + "kardinal.kontrol/kardinal-manager/cluster_manager" + "kardinal.kontrol/kardinal-manager/utils" + "net/http" + "time" +) + +const ( + defaultTickerDuration = time.Second * 5 + fetcherJobDurationSecondsEnvVarKey = "FETCHER_JOB_DURATION_SECONDS" +) + +type fetcher struct { + clusterManager *cluster_manager.ClusterManager + configEndpoint string +} + +func NewFetcher(clusterManager *cluster_manager.ClusterManager, configEndpoint string) *fetcher { + return &fetcher{clusterManager: clusterManager, configEndpoint: configEndpoint} +} + +func (fetcher *fetcher) Run(ctx context.Context) error { + + fetcherTickerDuration := defaultTickerDuration + + fetcherJobDurationSecondsEnVarValue, err := utils.GetIntFromEnvVar(fetcherJobDurationSecondsEnvVarKey, "fetcher job duration seconds") + if err != nil { + logrus.Debugf("an error occurred while getting the fetcher job durations seconds from the env var, using default value '%s'. Error:\n%s", defaultTickerDuration, err) + } + + if fetcherJobDurationSecondsEnVarValue != 0 { + fetcherTickerDuration = time.Second * time.Duration(int64(fetcherJobDurationSecondsEnVarValue)) + } + + ticker := time.NewTicker(fetcherTickerDuration) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + logrus.Debugf("New fetcher execution at %s", time.Now()) + if err := fetcher.fetchAndApply(ctx); err != nil { + return stacktrace.Propagate(err, "Failed to fetch and apply the cluster configuration") + } + } + } +} + +func (fetcher *fetcher) fetchAndApply(ctx context.Context) error { + clusterResources, err := fetcher.getClusterResourcesFromCloud() + if err != nil { + return stacktrace.Propagate(err, "An error occurred fetching cluster resources from cloud") + } + + if err = fetcher.clusterManager.ApplyClusterResources(ctx, clusterResources); err != nil { + return stacktrace.Propagate(err, "Failed to apply cluster resources '%+v'", clusterResources) + } + + if err = fetcher.clusterManager.CleanUpClusterResources(ctx, clusterResources); err != nil { + return stacktrace.Propagate(err, "Failed to clean up cluster resources '%+v'", clusterResources) + } + + return nil +} + +func (fetcher *fetcher) getClusterResourcesFromCloud() (*types.ClusterResources, error) { + resp, err := http.Get(fetcher.configEndpoint) + if err != nil { + return nil, stacktrace.Propagate(err, "Error fetching cluster resources from endpoint '%s'", fetcher.configEndpoint) + } + defer resp.Body.Close() + + responseBodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, stacktrace.Propagate(err, "Error reading the response from '%v'", fetcher.configEndpoint) + } + + var clusterResources *types.ClusterResources + + if err = json.Unmarshal(responseBodyBytes, &clusterResources); err != nil { + return nil, stacktrace.Propagate(err, "And error occurred unmarshalling the response to a config response object") + } + + return clusterResources, nil +} diff --git a/kardinal-manager/kardinal-manager/fetcher/fetcher_test.go b/kardinal-manager/kardinal-manager/fetcher/fetcher_test.go new file mode 100644 index 00000000..f505955e --- /dev/null +++ b/kardinal-manager/kardinal-manager/fetcher/fetcher_test.go @@ -0,0 +1,43 @@ +package fetcher + +import ( + "context" + "github.com/stretchr/testify/require" + "kardinal.kontrol/kardinal-manager/cluster_manager" + "testing" +) + +// This test can be executed and use Minikube dashboard and Kiali Dashboard to see the changes between prod apply and devInProd apply +// these code is meant for local iteration for now and less for unit testing +func TestVotingAppDemoProdAndDevCase(t *testing.T) { + clusterManager, err := cluster_manager.CreateClusterManager() + require.NoError(t, err) + + prodOnlyDemoConfigEndpoint := "https://gist.githubusercontent.com/leoporoli/477b9b95238ffa994fb62849debb9abc/raw/b911cbe28df8cb65bf84834f666f94488937c364/cluster-resources-examples.json" + + prodFetcher := NewFetcher(clusterManager, prodOnlyDemoConfigEndpoint) + + ctx := context.Background() + + err = prodFetcher.fetchAndApply(ctx) + require.NoError(t, err) + + // Sleep to check the Cluster topology in Minikube and Kiali, prod topology should be created in voting-app namespace + //time.Sleep(2 * time.Minute) + + devInProdEndpoint := "https://gist.githubusercontent.com/leoporoli/d3e3afb29fa0dcc12738df558b263154/raw/7da19c18d34edf09bd2fe2939134b1d0424d1c2b/cluster-resources-for-dev.json" + + devInProdFetcher := NewFetcher(clusterManager, devInProdEndpoint) + + err = devInProdFetcher.fetchAndApply(ctx) + require.NoError(t, err) + + // Sleep to check the Cluster topology in Minikube and Kiali, dev topology should be added in voting-app namespace + //time.Sleep(2 * time.Minute) + + //Executing prodFetcher again to remove the Dev resources + //err = prodFetcher.fetchAndApply(ctx) + //require.NoError(t, err) + + // Now you can check that dev components has been removed from the cluster +} diff --git a/kardinal-manager/kardinal-manager/logger/logger.go b/kardinal-manager/kardinal-manager/logger/logger.go new file mode 100644 index 00000000..9333d902 --- /dev/null +++ b/kardinal-manager/kardinal-manager/logger/logger.go @@ -0,0 +1,88 @@ +package logger + +import ( + "github.com/kurtosis-tech/stacktrace" + "github.com/sirupsen/logrus" + "kardinal.kontrol/kardinal-manager/utils" + "path" + "runtime" + "strings" +) + +const ( + forceColors = true + fullTimestamp = true + + logMethodAlongWithLogLine = true + functionPathSeparator = "." + emptyFunctionName = "" + + loggerLogLeverEnvVarKey = "LOGGER_LOG_LEVEL" + + defaultLogLevel = logrus.DebugLevel +) + +func ConfigureLogger() error { + + if err := SetLevel(); err != nil { + return stacktrace.Propagate(err, "an error occurred setting the Logrus log level") + } + + // This allows the filename & function to be reported + logrus.SetReportCaller(logMethodAlongWithLogLine) + // NOTE: we'll want to change the ForceColors to false if we ever want structured logging + logrus.SetFormatter(&logrus.TextFormatter{ + ForceColors: forceColors, + DisableColors: false, + ForceQuote: false, + DisableQuote: false, + EnvironmentOverrideColors: false, + DisableTimestamp: false, + FullTimestamp: fullTimestamp, + TimestampFormat: "", + DisableSorting: false, + SortingFunc: nil, + DisableLevelTruncation: false, + PadLevelText: false, + QuoteEmptyFields: false, + FieldMap: nil, + CallerPrettyfier: func(f *runtime.Frame) (string, string) { + fullFunctionPath := strings.Split(f.Function, functionPathSeparator) + functionName := fullFunctionPath[len(fullFunctionPath)-1] + _, filename := path.Split(f.File) + return emptyFunctionName, formatFilenameFunctionForLogs(filename, functionName) + }, + }) + return nil +} + +func SetLevel() error { + + logrusLogLevel := defaultLogLevel + + loggerLogLevelEnVarValue, err := utils.GetFromEnvVar(loggerLogLeverEnvVarKey, "Logger log level") + if err != nil { + logrus.Debugf("an error occurred while getting the logger log level value from the env var, using default value %s. Error:\n%s", defaultLogLevel, err) + } + + if loggerLogLevelEnVarValue != "" { + logrusLogLevel, err = logrus.ParseLevel(loggerLogLevelEnVarValue) + if err != nil { + return stacktrace.Propagate(err, "an error occurred parsing log level string '%s' to a Logrus log level", loggerLogLevelEnVarValue) + } + } + + logrus.SetLevel(logrusLogLevel) + + return nil +} + +func formatFilenameFunctionForLogs(filename string, functionName string) string { + var output strings.Builder + output.WriteString("[") + output.WriteString(filename) + output.WriteString(":") + output.WriteString(functionName) + output.WriteString("]") + return output.String() +} diff --git a/kardinal-manager/kardinal-manager/logger/logger_test.go b/kardinal-manager/kardinal-manager/logger/logger_test.go new file mode 100644 index 00000000..9a27a1eb --- /dev/null +++ b/kardinal-manager/kardinal-manager/logger/logger_test.go @@ -0,0 +1,39 @@ +package logger + +import ( + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "os" + "strings" + "testing" +) + +func TestSetLoggerLevel_Success(t *testing.T) { + validLogLevels := []string{"panic", "fatal", "error", "warning", "info", "debug", "trace"} + + for _, logLevelStr := range validLogLevels { + // lowercase + os.Setenv(loggerLogLeverEnvVarKey, logLevelStr) + err := SetLevel() + require.NoError(t, err) + logrusLevel := logrus.GetLevel() + + require.Equal(t, logLevelStr, logrusLevel.String()) + + // uppercase + os.Setenv(loggerLogLeverEnvVarKey, strings.ToUpper(logLevelStr)) + err = SetLevel() + require.NoError(t, err) + logrusLevel = logrus.GetLevel() + + require.Equal(t, logLevelStr, logrusLevel.String()) + } + +} + +func TestSetLoggerLevel_WrongLevel(t *testing.T) { + levelStr := "wrong" + os.Setenv(loggerLogLeverEnvVarKey, levelStr) + err := SetLevel() + require.Error(t, err) +} diff --git a/kardinal-manager/kardinal-manager/main.go b/kardinal-manager/kardinal-manager/main.go new file mode 100644 index 00000000..a1e94b35 --- /dev/null +++ b/kardinal-manager/kardinal-manager/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "context" + "github.com/sirupsen/logrus" + "kardinal.kontrol/kardinal-manager/cluster_manager" + "kardinal.kontrol/kardinal-manager/fetcher" + "kardinal.kontrol/kardinal-manager/logger" + "kardinal.kontrol/kardinal-manager/utils" + "os" +) + +const ( + successExitCode = 0 + clusterConfigEndpointEnvVarKey = "CLUSTER_CONFIG_ENDPOINT" +) + +func main() { + + // Create context + ctx := context.Background() + + if err := logger.ConfigureLogger(); err != nil { + logrus.Fatal("An error occurred configuring the logger!\nError was: %s", err) + } + + configEndpoint, err := utils.GetFromEnvVar(clusterConfigEndpointEnvVarKey, "the config endpoint") + if err != nil { + logrus.Fatal("An error occurred getting the config endpoint from the env vars!\nError was: %s", err) + } + + clusterManager, err := cluster_manager.CreateClusterManager() + if err != nil { + logrus.Fatal("An error occurred while creating the cluster manager!\nError was: %s", err) + } + + fetcher := fetcher.NewFetcher(clusterManager, configEndpoint) + + if err = fetcher.Run(ctx); err != nil { + logrus.Fatalf("An error occurred while running the fetcher!\nError was: %s", err) + } + + // No external clients connection so-far + //if err := server.CreateAndStartRestAPIServer(); err != nil { + // logrus.Fatalf("The REST API server is down, exiting!\nError was: %s", err) + //} + + os.Exit(successExitCode) +} diff --git a/kardinal-manager/kardinal-manager/server/server.go b/kardinal-manager/kardinal-manager/server/server.go new file mode 100644 index 00000000..65639dae --- /dev/null +++ b/kardinal-manager/kardinal-manager/server/server.go @@ -0,0 +1,48 @@ +package server + +import ( + "context" + rest_api "kardinal.kontrol/kardinal-manager/api/http_rest/server" + rest_types "kardinal.kontrol/kardinal-manager/api/http_rest/types" +) + +type Server struct{} + +func NewServer() Server { + return Server{} +} + +// List virtual services +// (GET /virtual-services) +func (Server) GetVirtualServices(ctx context.Context, object rest_api.GetVirtualServicesRequestObject) (rest_api.GetVirtualServicesResponseObject, error) { + response := map[string]rest_types.VirtualService{ + "fake-virtual-service-01": { + Name: "fake-virtual-service-01", + }, + "fake-virtual-service-02": { + Name: "fake-virtual-service-02", + }, + } + + return rest_api.GetVirtualServices200JSONResponse(response), nil +} + +// Create virtual service +// (POST /virtual-services) +func (Server) PostVirtualServices(ctx context.Context, object rest_api.PostVirtualServicesRequestObject) (rest_api.PostVirtualServicesResponseObject, error) { + response := rest_types.VirtualService{ + Name: "fake-virtual-service-01", + } + + return rest_api.PostVirtualServices200JSONResponse(response), nil +} + +// Delete virtual service +// (DELETE /virtual-services) +func (Server) DeleteVirtualServices(ctx context.Context, object rest_api.DeleteVirtualServicesRequestObject) (rest_api.DeleteVirtualServicesResponseObject, error) { + response := rest_types.VirtualService{ + Name: "fake-virtual-service-01", + } + + return rest_api.DeleteVirtualServices200JSONResponse(response), nil +} diff --git a/kardinal-manager/kardinal-manager/server/server_factory.go b/kardinal-manager/kardinal-manager/server/server_factory.go new file mode 100644 index 00000000..483b9f9e --- /dev/null +++ b/kardinal-manager/kardinal-manager/server/server_factory.go @@ -0,0 +1,42 @@ +package server + +import ( + "fmt" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "github.com/sirupsen/logrus" + kardinal_manager_server_rest_server "kardinal.kontrol/kardinal-manager/api/http_rest/server" + "net" +) + +const ( + pathToApiGroup = "/api" + restAPIPortAddr uint16 = 8080 + restAPIHostIP string = "0.0.0.0" +) + +var ( + defaultCORSOrigins = []string{"*"} + defaultCORSHeaders = []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept} +) + +func CreateAndStartRestAPIServer() error { + logrus.Info("Running REST API server...") + + // This is how you set up a basic Echo router + echoRouter := echo.New() + echoApiRouter := echoRouter.Group(pathToApiGroup) + echoApiRouter.Use(middleware.Logger()) + + // CORS configuration + echoApiRouter.Use(middleware.CORSWithConfig(middleware.CORSConfig{ + AllowOrigins: defaultCORSOrigins, + AllowHeaders: defaultCORSHeaders, + })) + + server := NewServer() + + kardinal_manager_server_rest_server.RegisterHandlers(echoApiRouter, kardinal_manager_server_rest_server.NewStrictHandler(server, nil)) + + return echoRouter.Start(net.JoinHostPort(restAPIHostIP, fmt.Sprint(restAPIPortAddr))) +} diff --git a/kardinal-manager/kardinal-manager/topology/toplogy.go b/kardinal-manager/kardinal-manager/topology/toplogy.go new file mode 100644 index 00000000..bc908d4d --- /dev/null +++ b/kardinal-manager/kardinal-manager/topology/toplogy.go @@ -0,0 +1,81 @@ +package topology + +import ( + "github.com/sirupsen/logrus" +) + +type RawKialiGraph struct { + Elements struct { + Nodes []struct { + Data struct { + ID string `json:"id"` + NodeType string `json:"nodeType"` + Service string `json:"service"` + App string `json:"app"` + Version string `json:"version"` + } `json:"data"` + } `json:"nodes"` + Edges []struct { + Data struct { + Source string `json:"source"` + Target string `json:"target"` + } `json:"data"` + } `json:"edges"` + } `json:"elements"` +} + +type Node struct { + RawKialiGraphID string + ID string // serviceName_version + ServiceName string + ServiceVersion string + TalksTo []string // List of IDs (serviceName_version) +} + +func graphToNodesMap(graph *RawKialiGraph) map[string]*Node { + nodesMap := make(map[string]*Node) + idMap := make(map[string]string) // Map from raw graph ID to readable ID + nodesByUsefulID := make(map[string]*Node) + + // Populate nodes + for _, n := range graph.Elements.Nodes { + serviceName := n.Data.Service + if serviceName == "" { + serviceName = n.Data.App // Use app name if service name is not specified + } + serviceVersion := n.Data.Version + if serviceVersion == "" { + serviceVersion = "latest" // Default to 'latest' if no version is specified + } + readableID := serviceName + "_" + serviceVersion + + node := &Node{ + RawKialiGraphID: n.Data.ID, + ID: readableID, + ServiceName: serviceName, + ServiceVersion: serviceVersion, + TalksTo: make([]string, 0), + } + nodesMap[n.Data.ID] = node + idMap[n.Data.ID] = readableID + nodesByUsefulID[readableID] = node + } + + // Populate connections using readable IDs + for _, e := range graph.Elements.Edges { + if sourceNode, ok := nodesMap[e.Data.Source]; ok { + if targetID, ok := idMap[e.Data.Target]; ok { + sourceNode.TalksTo = append(sourceNode.TalksTo, targetID) + } + } + } + + // Print the nodes and their connections + for _, node := range nodesMap { + logrus.Debugf("Node ID: %s (RawKialiGraphID: %s)", node.ID, node.RawKialiGraphID) + logrus.Debugf(" Service: %s Version: %s", node.ServiceName, node.ServiceVersion) + logrus.Debugf(" Talks To: %v", node.TalksTo) + } + + return nodesByUsefulID +} diff --git a/kardinal-manager/kardinal-manager/topology/topology_manager.go b/kardinal-manager/kardinal-manager/topology/topology_manager.go new file mode 100644 index 00000000..42a39584 --- /dev/null +++ b/kardinal-manager/kardinal-manager/topology/topology_manager.go @@ -0,0 +1,146 @@ +package topology + +import ( + "context" + "encoding/json" + "fmt" + "github.com/sirupsen/logrus" + "io/ioutil" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/portforward" + "k8s.io/client-go/transport/spdy" + "net/http" + "os" +) + +const ( + kialiServiceName = "kiali" + namespaceName = "istio-system" +) + +type Manager struct { + k8sConfig *rest.Config +} + +func NewTopologyManager(k8sConfig *rest.Config) *Manager { + return &Manager{k8sConfig: k8sConfig} +} + +func (tf *Manager) FetchTopology(namespace string) (map[string]*Node, error) { + stopChan, readyChan := make(chan struct{}, 1), make(chan struct{}, 1) + go func() { + err := setupPortForwarding(tf.k8sConfig, stopChan, readyChan) + if err != nil { + logrus.Fatalf("Error setting up port forwarding: %v", err) + } + }() + <-readyChan // Wait for port forwarding to be ready + + defer close(stopChan) + + var graph map[string]*Node + + // Fetch the graph data + graph, err := fetchGraphData(namespace) + if err != nil { + return nil, fmt.Errorf("Error fetching graph data: %v", err) + } + return graph, nil +} + +func fetchGraphData(namespace string) (map[string]*Node, error) { + fmt.Printf("Fetching graph data for namespace %s...\n", namespace) + + url := fmt.Sprintf("http://localhost:20001/kiali/api/namespaces/graph?duration=60s&graphType=versionedApp&includeIdleEdges=false&injectServiceNodes=true&boxBy=cluster,namespace&appenders=deadNode,istio,serviceEntry,meshCheck,workloadEntry,health&rateGrpc=requests&rateHttp=requests&rateTcp=sent&namespaces=%s", namespace) + resp, err := http.Get(url) + if err != nil { + return nil, fmt.Errorf("failed to fetch graph data: %v", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("failed to fetch graph data: %s", resp.Status) + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read response body: %v", err) + } + + var graph RawKialiGraph + + if err := json.Unmarshal(body, &graph); err != nil { + return nil, fmt.Errorf("failed to convert response body into inner representation") + } + + return graphToNodesMap(&graph), nil +} + +// setupPortForwarding this sets up a port forward for Kiali; +// TODO make this less fragile +func setupPortForwarding(config *rest.Config, stopChan, readyChan chan struct{}) error { + roundTripper, upgrader, err := spdy.RoundTripperFor(config) + if err != nil { + return fmt.Errorf("error creating round tripper: %v", err) + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return fmt.Errorf("error creating Kubernetes client: %v", err) + } + + podName, err := getPodsForSvc(kialiServiceName, namespaceName, clientset) + if err != nil { + return err + } + + // Define the port forwarding request + req := clientset.CoreV1().RESTClient().Post(). + Resource("pods"). + Namespace(namespaceName). + Name(podName). + SubResource("portforward") + + // Set up the port forwarding options + ports := []string{"20001:20001"} + dialer := spdy.NewDialer(upgrader, &http.Client{Transport: roundTripper}, "POST", req.URL()) + + // Create the port forwarder + fw, err := portforward.New(dialer, ports, stopChan, readyChan, os.Stdout, os.Stderr) + if err != nil { + return fmt.Errorf("error creating port forwarder: %v", err) + } + + // Start port forwarding + err = fw.ForwardPorts() + if err != nil { + return fmt.Errorf("error forwarding ports: %v", err) + } + + return nil +} + +func getPodsForSvc(serviceName string, namespace string, clientset *kubernetes.Clientset) (string, error) { + svc, err := clientset.CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{}) + if err != nil { + panic(err.Error()) + } + + // Use the service's selectors to find the pods + selector := labels.SelectorFromSet(svc.Spec.Selector) + pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{ + LabelSelector: selector.String(), + }) + if err != nil { + panic(err.Error()) + } + + if len(pods.Items) == 0 { + return "", fmt.Errorf("Couldn't find a pod for service '%v' in name space '%v", serviceName, namespace) + } + + return pods.Items[0].Name, nil +} diff --git a/kardinal-manager/kardinal-manager/utils/utils.go b/kardinal-manager/kardinal-manager/utils/utils.go new file mode 100644 index 00000000..8be4cc8d --- /dev/null +++ b/kardinal-manager/kardinal-manager/utils/utils.go @@ -0,0 +1,35 @@ +package utils + +import ( + "github.com/kurtosis-tech/stacktrace" + "github.com/sirupsen/logrus" + "os" + "strconv" +) + +func GetFromEnvVar( + key string, + subject string, +) (string, error) { + value := os.Getenv(key) + if len(value) < 1 { + return "", stacktrace.NewError("No '%s' env var was found. Must be provided as env var %s", subject, key) + } + logrus.Debugf("Successfully loaded env var '%s'", subject) + return value, nil +} + +func GetIntFromEnvVar( + key string, + subject string, +) (int, error) { + strVal, err := GetFromEnvVar(key, subject) + if err != nil { + return 0, stacktrace.Propagate(err, "An error occurred getting env var with key '%s'", key) + } + intVal, err := strconv.Atoi(strVal) + if err != nil { + return 0, stacktrace.Propagate(err, "An error occurred converting string value '%s' to int", strVal) + } + return intVal, nil +} diff --git a/kardinal-manager/shell.nix b/kardinal-manager/shell.nix new file mode 100644 index 00000000..894c97d9 --- /dev/null +++ b/kardinal-manager/shell.nix @@ -0,0 +1,17 @@ +{pkgs}: let + goEnv = pkgs.mkGoEnv {pwd = ./.;}; +in + pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + goEnv + + goreleaser + go + gopls + golangci-lint + delve + enumer + gomod2nix + bash-completion + ]; + } diff --git a/kardinal-manager/tools/tools.go b/kardinal-manager/tools/tools.go new file mode 100644 index 00000000..ac506ec6 --- /dev/null +++ b/kardinal-manager/tools/tools.go @@ -0,0 +1,9 @@ +//go:build tools +// +build tools + +package main + +// It follows the `tools.go` pattern described here: https://github.com/deepmap/oapi-codegen?tab=readme-ov-file#install so we can run the codegen without the need to install the binary +import ( + _ "github.com/deepmap/oapi-codegen/v2/cmd/oapi-codegen" +) diff --git a/kardinal-manager/vs-example.yaml b/kardinal-manager/vs-example.yaml new file mode 100644 index 00000000..57f2a72a --- /dev/null +++ b/kardinal-manager/vs-example.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.istio.io/v1alpha3 +kind: VirtualService +metadata: + name: reviews +spec: + hosts: + - reviews + http: +# - match: +# - uri: +# prefix: "/reviews/0" +# route: +# - destination: +# host: reviews +# subset: v2 + - route: + - destination: + host: reviews + subset: v1 \ No newline at end of file diff --git a/libs/cli-kontrol-api/README.md b/libs/cli-kontrol-api/README.md new file mode 100644 index 00000000..a87cb0ed --- /dev/null +++ b/libs/cli-kontrol-api/README.md @@ -0,0 +1,9 @@ +# CLI-Kontrol API + +The OpenAPI specs are located in the `specs` directory. Whenever the specs are updated, the REST API bindings should be regenerated. This can be done by running the following command: + +```bash +# Make sure your are inside a nix shell (nix develop) +./scripts/build.sh +''' +``` diff --git a/libs/cli-kontrol-api/api/golang/client/client.gen.go b/libs/cli-kontrol-api/api/golang/client/client.gen.go new file mode 100644 index 00000000..65633a46 --- /dev/null +++ b/libs/cli-kontrol-api/api/golang/client/client.gen.go @@ -0,0 +1,677 @@ +// Package client provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version 2.1.0 DO NOT EDIT. +package client + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + . "github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api/api/golang/types" + "github.com/oapi-codegen/runtime" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // PostDeployWithBody request with any body + PostDeployWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostDeploy(ctx context.Context, body PostDeployJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFlowCreateWithBody request with any body + PostFlowCreateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostFlowCreate(ctx context.Context, body PostFlowCreateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFlowDeleteWithBody request with any body + PostFlowDeleteWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostFlowDelete(ctx context.Context, body PostFlowDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTopology request + GetTopology(ctx context.Context, params *GetTopologyParams, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) PostDeployWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostDeployRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostDeploy(ctx context.Context, body PostDeployJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostDeployRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostFlowCreateWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFlowCreateRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostFlowCreate(ctx context.Context, body PostFlowCreateJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFlowCreateRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostFlowDeleteWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFlowDeleteRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostFlowDelete(ctx context.Context, body PostFlowDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFlowDeleteRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetTopology(ctx context.Context, params *GetTopologyParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTopologyRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewPostDeployRequest calls the generic PostDeploy builder with application/json body +func NewPostDeployRequest(server string, body PostDeployJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostDeployRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostDeployRequestWithBody generates requests for PostDeploy with any type of body +func NewPostDeployRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/deploy") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostFlowCreateRequest calls the generic PostFlowCreate builder with application/json body +func NewPostFlowCreateRequest(server string, body PostFlowCreateJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostFlowCreateRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostFlowCreateRequestWithBody generates requests for PostFlowCreate with any type of body +func NewPostFlowCreateRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/flow/create") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostFlowDeleteRequest calls the generic PostFlowDelete builder with application/json body +func NewPostFlowDeleteRequest(server string, body PostFlowDeleteJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostFlowDeleteRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostFlowDeleteRequestWithBody generates requests for PostFlowDelete with any type of body +func NewPostFlowDeleteRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/flow/delete") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetTopologyRequest generates requests for GetTopology +func NewGetTopologyRequest(server string, params *GetTopologyParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/topology") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Namespace != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "namespace", runtime.ParamLocationQuery, *params.Namespace); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // PostDeployWithBodyWithResponse request with any body + PostDeployWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostDeployResponse, error) + + PostDeployWithResponse(ctx context.Context, body PostDeployJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDeployResponse, error) + + // PostFlowCreateWithBodyWithResponse request with any body + PostFlowCreateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFlowCreateResponse, error) + + PostFlowCreateWithResponse(ctx context.Context, body PostFlowCreateJSONRequestBody, reqEditors ...RequestEditorFn) (*PostFlowCreateResponse, error) + + // PostFlowDeleteWithBodyWithResponse request with any body + PostFlowDeleteWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFlowDeleteResponse, error) + + PostFlowDeleteWithResponse(ctx context.Context, body PostFlowDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*PostFlowDeleteResponse, error) + + // GetTopologyWithResponse request + GetTopologyWithResponse(ctx context.Context, params *GetTopologyParams, reqEditors ...RequestEditorFn) (*GetTopologyResponse, error) +} + +type PostDeployResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *string +} + +// Status returns HTTPResponse.Status +func (r PostDeployResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostDeployResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PostFlowCreateResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *string +} + +// Status returns HTTPResponse.Status +func (r PostFlowCreateResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFlowCreateResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type PostFlowDeleteResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *string +} + +// Status returns HTTPResponse.Status +func (r PostFlowDeleteResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFlowDeleteResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetTopologyResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ClusterTopology +} + +// Status returns HTTPResponse.Status +func (r GetTopologyResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTopologyResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// PostDeployWithBodyWithResponse request with arbitrary body returning *PostDeployResponse +func (c *ClientWithResponses) PostDeployWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostDeployResponse, error) { + rsp, err := c.PostDeployWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostDeployResponse(rsp) +} + +func (c *ClientWithResponses) PostDeployWithResponse(ctx context.Context, body PostDeployJSONRequestBody, reqEditors ...RequestEditorFn) (*PostDeployResponse, error) { + rsp, err := c.PostDeploy(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostDeployResponse(rsp) +} + +// PostFlowCreateWithBodyWithResponse request with arbitrary body returning *PostFlowCreateResponse +func (c *ClientWithResponses) PostFlowCreateWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFlowCreateResponse, error) { + rsp, err := c.PostFlowCreateWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFlowCreateResponse(rsp) +} + +func (c *ClientWithResponses) PostFlowCreateWithResponse(ctx context.Context, body PostFlowCreateJSONRequestBody, reqEditors ...RequestEditorFn) (*PostFlowCreateResponse, error) { + rsp, err := c.PostFlowCreate(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFlowCreateResponse(rsp) +} + +// PostFlowDeleteWithBodyWithResponse request with arbitrary body returning *PostFlowDeleteResponse +func (c *ClientWithResponses) PostFlowDeleteWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFlowDeleteResponse, error) { + rsp, err := c.PostFlowDeleteWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFlowDeleteResponse(rsp) +} + +func (c *ClientWithResponses) PostFlowDeleteWithResponse(ctx context.Context, body PostFlowDeleteJSONRequestBody, reqEditors ...RequestEditorFn) (*PostFlowDeleteResponse, error) { + rsp, err := c.PostFlowDelete(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFlowDeleteResponse(rsp) +} + +// GetTopologyWithResponse request returning *GetTopologyResponse +func (c *ClientWithResponses) GetTopologyWithResponse(ctx context.Context, params *GetTopologyParams, reqEditors ...RequestEditorFn) (*GetTopologyResponse, error) { + rsp, err := c.GetTopology(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTopologyResponse(rsp) +} + +// ParsePostDeployResponse parses an HTTP response from a PostDeployWithResponse call +func ParsePostDeployResponse(rsp *http.Response) (*PostDeployResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostDeployResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest string + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParsePostFlowCreateResponse parses an HTTP response from a PostFlowCreateWithResponse call +func ParsePostFlowCreateResponse(rsp *http.Response) (*PostFlowCreateResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFlowCreateResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest string + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParsePostFlowDeleteResponse parses an HTTP response from a PostFlowDeleteWithResponse call +func ParsePostFlowDeleteResponse(rsp *http.Response) (*PostFlowDeleteResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFlowDeleteResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest string + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseGetTopologyResponse parses an HTTP response from a GetTopologyWithResponse call +func ParseGetTopologyResponse(rsp *http.Response) (*GetTopologyResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTopologyResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ClusterTopology + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} diff --git a/libs/cli-kontrol-api/api/golang/server/server.gen.go b/libs/cli-kontrol-api/api/golang/server/server.gen.go new file mode 100644 index 00000000..cf5b53f0 --- /dev/null +++ b/libs/cli-kontrol-api/api/golang/server/server.gen.go @@ -0,0 +1,422 @@ +// Package server provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version 2.1.0 DO NOT EDIT. +package server + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + . "github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api/api/golang/types" + "github.com/labstack/echo/v4" + "github.com/oapi-codegen/runtime" + strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + + // (POST /deploy) + PostDeploy(ctx echo.Context) error + + // (POST /flow/create) + PostFlowCreate(ctx echo.Context) error + + // (POST /flow/delete) + PostFlowDelete(ctx echo.Context) error + + // (GET /topology) + GetTopology(ctx echo.Context, params GetTopologyParams) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// PostDeploy converts echo context to params. +func (w *ServerInterfaceWrapper) PostDeploy(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostDeploy(ctx) + return err +} + +// PostFlowCreate converts echo context to params. +func (w *ServerInterfaceWrapper) PostFlowCreate(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostFlowCreate(ctx) + return err +} + +// PostFlowDelete converts echo context to params. +func (w *ServerInterfaceWrapper) PostFlowDelete(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostFlowDelete(ctx) + return err +} + +// GetTopology converts echo context to params. +func (w *ServerInterfaceWrapper) GetTopology(ctx echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetTopologyParams + // ------------- Optional query parameter "namespace" ------------- + + err = runtime.BindQueryParameter("form", true, false, "namespace", ctx.QueryParams(), ¶ms.Namespace) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter namespace: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetTopology(ctx, params) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.POST(baseURL+"/deploy", wrapper.PostDeploy) + router.POST(baseURL+"/flow/create", wrapper.PostFlowCreate) + router.POST(baseURL+"/flow/delete", wrapper.PostFlowDelete) + router.GET(baseURL+"/topology", wrapper.GetTopology) + +} + +type PostDeployRequestObject struct { + Body *PostDeployJSONRequestBody +} + +type PostDeployResponseObject interface { + VisitPostDeployResponse(w http.ResponseWriter) error +} + +type PostDeploy200JSONResponse string + +func (response PostDeploy200JSONResponse) VisitPostDeployResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostFlowCreateRequestObject struct { + Body *PostFlowCreateJSONRequestBody +} + +type PostFlowCreateResponseObject interface { + VisitPostFlowCreateResponse(w http.ResponseWriter) error +} + +type PostFlowCreate200JSONResponse string + +func (response PostFlowCreate200JSONResponse) VisitPostFlowCreateResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostFlowDeleteRequestObject struct { + Body *PostFlowDeleteJSONRequestBody +} + +type PostFlowDeleteResponseObject interface { + VisitPostFlowDeleteResponse(w http.ResponseWriter) error +} + +type PostFlowDelete200JSONResponse string + +func (response PostFlowDelete200JSONResponse) VisitPostFlowDeleteResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetTopologyRequestObject struct { + Params GetTopologyParams +} + +type GetTopologyResponseObject interface { + VisitGetTopologyResponse(w http.ResponseWriter) error +} + +type GetTopology200JSONResponse ClusterTopology + +func (response GetTopology200JSONResponse) VisitGetTopologyResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + + // (POST /deploy) + PostDeploy(ctx context.Context, request PostDeployRequestObject) (PostDeployResponseObject, error) + + // (POST /flow/create) + PostFlowCreate(ctx context.Context, request PostFlowCreateRequestObject) (PostFlowCreateResponseObject, error) + + // (POST /flow/delete) + PostFlowDelete(ctx context.Context, request PostFlowDeleteRequestObject) (PostFlowDeleteResponseObject, error) + + // (GET /topology) + GetTopology(ctx context.Context, request GetTopologyRequestObject) (GetTopologyResponseObject, error) +} + +type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc +type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// PostDeploy operation middleware +func (sh *strictHandler) PostDeploy(ctx echo.Context) error { + var request PostDeployRequestObject + + var body PostDeployJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostDeploy(ctx.Request().Context(), request.(PostDeployRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostDeploy") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostDeployResponseObject); ok { + return validResponse.VisitPostDeployResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostFlowCreate operation middleware +func (sh *strictHandler) PostFlowCreate(ctx echo.Context) error { + var request PostFlowCreateRequestObject + + var body PostFlowCreateJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostFlowCreate(ctx.Request().Context(), request.(PostFlowCreateRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostFlowCreate") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostFlowCreateResponseObject); ok { + return validResponse.VisitPostFlowCreateResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// PostFlowDelete operation middleware +func (sh *strictHandler) PostFlowDelete(ctx echo.Context) error { + var request PostFlowDeleteRequestObject + + var body PostFlowDeleteJSONRequestBody + if err := ctx.Bind(&body); err != nil { + return err + } + request.Body = &body + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostFlowDelete(ctx.Request().Context(), request.(PostFlowDeleteRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostFlowDelete") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(PostFlowDeleteResponseObject); ok { + return validResponse.VisitPostFlowDeleteResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// GetTopology operation middleware +func (sh *strictHandler) GetTopology(ctx echo.Context, params GetTopologyParams) error { + var request GetTopologyRequestObject + + request.Params = params + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetTopology(ctx.Request().Context(), request.(GetTopologyRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetTopology") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(GetTopologyResponseObject); ok { + return validResponse.VisitGetTopologyResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/9RWTW/DNgz9K4K2wwbEcbbdfNvSbShaDAXanYoeFIlx1NqiStFpgyL/fZDkfLtdimFY", + "d7MlvkfykSb9JjW2Hh04DrJ6k0EvoFXpcdp0gYHu0GOD9SoeeUIPxBaSAZg6P1iGNj18SzCXlfym3JGW", + "PWP5q6lBrkeSVx5kJRWRWsV3h+YTLH+gGWBZjyTBc2cJjKzue8pRH+DD1hpnj6A5wi9g+VuDL7ce9Gle", + "BvUTUJG8BzgI7bWosejZ+vvxLdDSapiim9tajnY2hW09EkecU+0eRI6kV7yQlawtL7rZWGNb9ndF8KC3", + "LzWWkSmkFI+Vs62qoWhQK0ZKBXlVrW+izUzpJ3CmUFWjGALLLTowWVdHeMhxFzm2IfTGQp3C1wOipgqf", + "qNmoGTRJVgiarGeLTlbyOh6LOZLgBYhYqfFgkNiRhlP43QKENeDYzi2QwHmiydYi1n9z9C4zK6qBz2XO", + "1ucwH/Vin8DW31A3pqY+Ec6a0+D+dPa5O4hvo2CMbDDPs/R/B32UizWD4d8Qmv/713Taz/HIujlGh2w5", + "fRjT68vyqsHOFFfomLARP99cypFcAoUs6w/jyXgSRUEPTnkrK/lTOspBprRLA77BPFAxpIyiYCpW5tLI", + "St5g4ItskwsAgX9BkxAaHYNLIOV9Y3WClY8hut/M77+bogcVS5keNkd2LpTwhEaga1ZC520g9zuCqYPU", + "IsGjC7ncP04mnwrzuOMGYlmKeYMvQhMkEhFYcReLGI3LeFemO/hY0ZjwNNv9O6ruL5WBRLJvoYTpM/qi", + "Whpo4BwtL7Ldf9Wh0flWSvEdwRKIRVxdgnHXuN9/PZV574+qX0GHCv8OvP3rinODVAsMFGR1P7St4jgM", + "XmlI4/xlYfUiSkDAZGEJeX/t+GzEPXdA8aUfpVsKOfog04d/qNxHBT/+2xzQdXMn4limNnlJmq7XfwUA", + "AP//eCMynscKAAA=", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/libs/cli-kontrol-api/api/golang/types/types.gen.go b/libs/cli-kontrol-api/api/golang/types/types.gen.go new file mode 100644 index 00000000..6fc923ee --- /dev/null +++ b/libs/cli-kontrol-api/api/golang/types/types.gen.go @@ -0,0 +1,62 @@ +// Package types provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version 2.1.0 DO NOT EDIT. +package types + +import ( + compose "github.com/compose-spec/compose-go/types" +) + +// ClusterTopology defines model for ClusterTopology. +type ClusterTopology struct { + Edges []Edge `json:"edges"` + Nodes []Node `json:"nodes"` +} + +// DevFlowSpec defines model for DevFlowSpec. +type DevFlowSpec struct { + DockerCompose *[]compose.ServiceConfig `json:"docker-compose,omitempty"` + ImageLocator *string `json:"image-locator,omitempty"` + ServiceName *string `json:"service-name,omitempty"` +} + +// Edge defines model for Edge. +type Edge struct { + // Label Label for the edge. + Label *string `json:"label,omitempty"` + + // Source The identifier of the source node of the edge. + Source string `json:"source"` + + // Target The identifier of the target node of the edge. + Target string `json:"target"` +} + +// Node defines model for Node. +type Node struct { + // Id Unique identifier for the node. + Id string `json:"id"` + + // Label Label for the node. + Label *string `json:"label,omitempty"` +} + +// ProdFlowSpec defines model for ProdFlowSpec. +type ProdFlowSpec struct { + DockerCompose *[]compose.ServiceConfig `json:"docker-compose,omitempty"` +} + +// GetTopologyParams defines parameters for GetTopology. +type GetTopologyParams struct { + // Namespace The namespace for which to retrieve the topology + Namespace *string `form:"namespace,omitempty" json:"namespace,omitempty"` +} + +// PostDeployJSONRequestBody defines body for PostDeploy for application/json ContentType. +type PostDeployJSONRequestBody = ProdFlowSpec + +// PostFlowCreateJSONRequestBody defines body for PostFlowCreate for application/json ContentType. +type PostFlowCreateJSONRequestBody = DevFlowSpec + +// PostFlowDeleteJSONRequestBody defines body for PostFlowDelete for application/json ContentType. +type PostFlowDeleteJSONRequestBody = ProdFlowSpec diff --git a/libs/cli-kontrol-api/api/typescript/client/types.d.ts b/libs/cli-kontrol-api/api/typescript/client/types.d.ts new file mode 100644 index 00000000..5574c1a7 --- /dev/null +++ b/libs/cli-kontrol-api/api/typescript/client/types.d.ts @@ -0,0 +1,126 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + + +export interface paths { + "/flow/create": { + post: { + /** @description Create a dev flow */ + requestBody: { + content: { + "application/json": components["schemas"]["DevFlowSpec"]; + }; + }; + responses: { + /** @description Dev flow creation status */ + 200: { + content: { + "application/json": string; + }; + }; + }; + }; + }; + "/flow/delete": { + post: { + /** @description Delete dev flow (revert back to prod only) */ + requestBody: { + content: { + "application/json": components["schemas"]["ProdFlowSpec"]; + }; + }; + responses: { + /** @description Dev flow creation status */ + 200: { + content: { + "application/json": string; + }; + }; + }; + }; + }; + "/deploy": { + post: { + /** @description Deploy a prod only cluster */ + requestBody: { + content: { + "application/json": components["schemas"]["ProdFlowSpec"]; + }; + }; + responses: { + /** @description Dev flow creation status */ + 200: { + content: { + "application/json": string; + }; + }; + }; + }; + }; + "/topology": { + get: { + parameters: { + query?: { + /** @description The namespace for which to retrieve the topology */ + namespace?: string; + }; + }; + responses: { + /** @description Topology information */ + 200: { + content: { + "application/json": components["schemas"]["ClusterTopology"]; + }; + }; + }; + }; + }; +} + +export type webhooks = Record; + +export interface components { + schemas: { + ProdFlowSpec: { + "docker-compose"?: unknown[]; + }; + DevFlowSpec: { + /** @example backend-a:latest */ + "image-locator"?: string; + /** @example backend-service-a */ + "service-name"?: string; + "docker-compose"?: unknown[]; + }; + Node: { + /** @description Unique identifier for the node. */ + id: string; + /** @description Label for the node. */ + label?: string; + }; + Edge: { + /** @description The identifier of the source node of the edge. */ + source: string; + /** @description The identifier of the target node of the edge. */ + target: string; + /** @description Label for the edge. */ + label?: string; + }; + ClusterTopology: { + nodes: components["schemas"]["Node"][]; + edges: components["schemas"]["Edge"][]; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export type external = Record; + +export type operations = Record; diff --git a/libs/cli-kontrol-api/dummy-server.py b/libs/cli-kontrol-api/dummy-server.py new file mode 100644 index 00000000..9516d296 --- /dev/null +++ b/libs/cli-kontrol-api/dummy-server.py @@ -0,0 +1,61 @@ +""" +License: MIT License +Copyright (c) 2023 Miel Donkers +Very simple HTTP server in python for logging requests +Usage:: + ./server.py [] +""" + +from http.server import BaseHTTPRequestHandler, HTTPServer +import logging + + +class S(BaseHTTPRequestHandler): + def _set_response(self): + self.send_response(200) + self.send_header("Content-type", "text/html") + self.end_headers() + + def do_GET(self): + logging.info( + "GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers) + ) + self._set_response() + self.wfile.write("GET request for {}".format(self.path).encode("utf-8")) + + def do_POST(self): + content_length = int( + self.headers["Content-Length"] + ) # <--- Gets the size of data + post_data = self.rfile.read(content_length) # <--- Gets the data itself + logging.info( + "POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n", + str(self.path), + str(self.headers), + post_data.decode("utf-8"), + ) + + self._set_response() + self.wfile.write("POST request for {}".format(self.path).encode("utf-8")) + + +def run(server_class=HTTPServer, handler_class=S, port=8080): + logging.basicConfig(level=logging.INFO) + server_address = ("", port) + httpd = server_class(server_address, handler_class) + logging.info("Starting httpd...\n") + try: + httpd.serve_forever() + except KeyboardInterrupt: + pass + httpd.server_close() + logging.info("Stopping httpd...\n") + + +if __name__ == "__main__": + from sys import argv + + if len(argv) == 2: + run(port=int(argv[1])) + else: + run() diff --git a/libs/cli-kontrol-api/generators/api_types.cfg.yaml b/libs/cli-kontrol-api/generators/api_types.cfg.yaml new file mode 100644 index 00000000..2473c54b --- /dev/null +++ b/libs/cli-kontrol-api/generators/api_types.cfg.yaml @@ -0,0 +1,4 @@ +package: types +generate: + models: true +output: api/golang/types/types.gen.go diff --git a/libs/cli-kontrol-api/generators/go_client.cfg.yaml b/libs/cli-kontrol-api/generators/go_client.cfg.yaml new file mode 100644 index 00000000..56ae84ab --- /dev/null +++ b/libs/cli-kontrol-api/generators/go_client.cfg.yaml @@ -0,0 +1,7 @@ +package: client +generate: + client: true +additional-imports: + - package: github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api/api/golang/types + alias: . +output: api/golang/client/client.gen.go diff --git a/libs/cli-kontrol-api/generators/go_server.cfg.yaml b/libs/cli-kontrol-api/generators/go_server.cfg.yaml new file mode 100644 index 00000000..abf1bb90 --- /dev/null +++ b/libs/cli-kontrol-api/generators/go_server.cfg.yaml @@ -0,0 +1,9 @@ +package: server +generate: + embedded-spec: true + echo-server: true + strict-server: true +additional-imports: + - package: github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api/api/golang/types + alias: . +output: api/golang/server/server.gen.go diff --git a/libs/cli-kontrol-api/go.mod b/libs/cli-kontrol-api/go.mod new file mode 100644 index 00000000..e8d2271f --- /dev/null +++ b/libs/cli-kontrol-api/go.mod @@ -0,0 +1,44 @@ +module github.com/kurtosis-tech/kardinal/libs/cli-kontrol-api + +go 1.22 + +require ( + github.com/compose-spec/compose-go v1.20.2 + github.com/getkin/kin-openapi v0.124.0 + github.com/labstack/echo/v4 v4.12.0 + github.com/oapi-codegen/runtime v1.1.1 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/distribution/reference v0.5.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/swag v0.22.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/stretchr/testify v1.9.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.23.0 // indirect + golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/libs/cli-kontrol-api/go.sum b/libs/cli-kontrol-api/go.sum new file mode 100644 index 00000000..4cf4998a --- /dev/null +++ b/libs/cli-kontrol-api/go.sum @@ -0,0 +1,103 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ= +github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M= +github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw= +golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= diff --git a/libs/cli-kontrol-api/nix/default.nix b/libs/cli-kontrol-api/nix/default.nix new file mode 100644 index 00000000..b795f708 --- /dev/null +++ b/libs/cli-kontrol-api/nix/default.nix @@ -0,0 +1,17 @@ +# This file has been generated by node2nix 1.11.1. Do not edit! + +{pkgs ? import { + inherit system; + }, system ? builtins.currentSystem, nodejs ? pkgs."nodejs_14"}: + +let + nodeEnv = import ./node-env.nix { + inherit (pkgs) stdenv lib python2 runCommand writeTextFile writeShellScript; + inherit pkgs nodejs; + libtool = if pkgs.stdenv.isDarwin then pkgs.darwin.cctools else null; + }; +in +import ./node-packages.nix { + inherit (pkgs) fetchurl nix-gitignore stdenv lib fetchgit; + inherit nodeEnv; +} diff --git a/libs/cli-kontrol-api/nix/node-env.nix b/libs/cli-kontrol-api/nix/node-env.nix new file mode 100644 index 00000000..bc1e3662 --- /dev/null +++ b/libs/cli-kontrol-api/nix/node-env.nix @@ -0,0 +1,689 @@ +# This file originates from node2nix + +{lib, stdenv, nodejs, python2, pkgs, libtool, runCommand, writeTextFile, writeShellScript}: + +let + # Workaround to cope with utillinux in Nixpkgs 20.09 and util-linux in Nixpkgs master + utillinux = if pkgs ? utillinux then pkgs.utillinux else pkgs.util-linux; + + python = if nodejs ? python then nodejs.python else python2; + + # Create a tar wrapper that filters all the 'Ignoring unknown extended header keyword' noise + tarWrapper = runCommand "tarWrapper" {} '' + mkdir -p $out/bin + + cat > $out/bin/tar <> $out/nix-support/hydra-build-products + ''; + }; + + # Common shell logic + installPackage = writeShellScript "install-package" '' + installPackage() { + local packageName=$1 src=$2 + + local strippedName + + local DIR=$PWD + cd $TMPDIR + + unpackFile $src + + # Make the base dir in which the target dependency resides first + mkdir -p "$(dirname "$DIR/$packageName")" + + if [ -f "$src" ] + then + # Figure out what directory has been unpacked + packageDir="$(find . -maxdepth 1 -type d | tail -1)" + + # Restore write permissions to make building work + find "$packageDir" -type d -exec chmod u+x {} \; + chmod -R u+w "$packageDir" + + # Move the extracted tarball into the output folder + mv "$packageDir" "$DIR/$packageName" + elif [ -d "$src" ] + then + # Get a stripped name (without hash) of the source directory. + # On old nixpkgs it's already set internally. + if [ -z "$strippedName" ] + then + strippedName="$(stripHash $src)" + fi + + # Restore write permissions to make building work + chmod -R u+w "$strippedName" + + # Move the extracted directory into the output folder + mv "$strippedName" "$DIR/$packageName" + fi + + # Change to the package directory to install dependencies + cd "$DIR/$packageName" + } + ''; + + # Bundle the dependencies of the package + # + # Only include dependencies if they don't exist. They may also be bundled in the package. + includeDependencies = {dependencies}: + lib.optionalString (dependencies != []) ( + '' + mkdir -p node_modules + cd node_modules + '' + + (lib.concatMapStrings (dependency: + '' + if [ ! -e "${dependency.packageName}" ]; then + ${composePackage dependency} + fi + '' + ) dependencies) + + '' + cd .. + '' + ); + + # Recursively composes the dependencies of a package + composePackage = { name, packageName, src, dependencies ? [], ... }@args: + builtins.addErrorContext "while evaluating node package '${packageName}'" '' + installPackage "${packageName}" "${src}" + ${includeDependencies { inherit dependencies; }} + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + ''; + + pinpointDependencies = {dependencies, production}: + let + pinpointDependenciesFromPackageJSON = writeTextFile { + name = "pinpointDependencies.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function resolveDependencyVersion(location, name) { + if(location == process.env['NIX_STORE']) { + return null; + } else { + var dependencyPackageJSON = path.join(location, "node_modules", name, "package.json"); + + if(fs.existsSync(dependencyPackageJSON)) { + var dependencyPackageObj = JSON.parse(fs.readFileSync(dependencyPackageJSON)); + + if(dependencyPackageObj.name == name) { + return dependencyPackageObj.version; + } + } else { + return resolveDependencyVersion(path.resolve(location, ".."), name); + } + } + } + + function replaceDependencies(dependencies) { + if(typeof dependencies == "object" && dependencies !== null) { + for(var dependency in dependencies) { + var resolvedVersion = resolveDependencyVersion(process.cwd(), dependency); + + if(resolvedVersion === null) { + process.stderr.write("WARNING: cannot pinpoint dependency: "+dependency+", context: "+process.cwd()+"\n"); + } else { + dependencies[dependency] = resolvedVersion; + } + } + } + } + + /* Read the package.json configuration */ + var packageObj = JSON.parse(fs.readFileSync('./package.json')); + + /* Pinpoint all dependencies */ + replaceDependencies(packageObj.dependencies); + if(process.argv[2] == "development") { + replaceDependencies(packageObj.devDependencies); + } + else { + packageObj.devDependencies = {}; + } + replaceDependencies(packageObj.optionalDependencies); + replaceDependencies(packageObj.peerDependencies); + + /* Write the fixed package.json file */ + fs.writeFileSync("package.json", JSON.stringify(packageObj, null, 2)); + ''; + }; + in + '' + node ${pinpointDependenciesFromPackageJSON} ${if production then "production" else "development"} + + ${lib.optionalString (dependencies != []) + '' + if [ -d node_modules ] + then + cd node_modules + ${lib.concatMapStrings (dependency: pinpointDependenciesOfPackage dependency) dependencies} + cd .. + fi + ''} + ''; + + # Recursively traverses all dependencies of a package and pinpoints all + # dependencies in the package.json file to the versions that are actually + # being used. + + pinpointDependenciesOfPackage = { packageName, dependencies ? [], production ? true, ... }@args: + '' + if [ -d "${packageName}" ] + then + cd "${packageName}" + ${pinpointDependencies { inherit dependencies production; }} + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + fi + ''; + + # Extract the Node.js source code which is used to compile packages with + # native bindings + nodeSources = runCommand "node-sources" {} '' + tar --no-same-owner --no-same-permissions -xf ${nodejs.src} + mv node-* $out + ''; + + # Script that adds _integrity fields to all package.json files to prevent NPM from consulting the cache (that is empty) + addIntegrityFieldsScript = writeTextFile { + name = "addintegrityfields.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + function augmentDependencies(baseDir, dependencies) { + for(var dependencyName in dependencies) { + var dependency = dependencies[dependencyName]; + + // Open package.json and augment metadata fields + var packageJSONDir = path.join(baseDir, "node_modules", dependencyName); + var packageJSONPath = path.join(packageJSONDir, "package.json"); + + if(fs.existsSync(packageJSONPath)) { // Only augment packages that exist. Sometimes we may have production installs in which development dependencies can be ignored + console.log("Adding metadata fields to: "+packageJSONPath); + var packageObj = JSON.parse(fs.readFileSync(packageJSONPath)); + + if(dependency.integrity) { + packageObj["_integrity"] = dependency.integrity; + } else { + packageObj["_integrity"] = "sha1-000000000000000000000000000="; // When no _integrity string has been provided (e.g. by Git dependencies), add a dummy one. It does not seem to harm and it bypasses downloads. + } + + if(dependency.resolved) { + packageObj["_resolved"] = dependency.resolved; // Adopt the resolved property if one has been provided + } else { + packageObj["_resolved"] = dependency.version; // Set the resolved version to the version identifier. This prevents NPM from cloning Git repositories. + } + + if(dependency.from !== undefined) { // Adopt from property if one has been provided + packageObj["_from"] = dependency.from; + } + + fs.writeFileSync(packageJSONPath, JSON.stringify(packageObj, null, 2)); + } + + // Augment transitive dependencies + if(dependency.dependencies !== undefined) { + augmentDependencies(packageJSONDir, dependency.dependencies); + } + } + } + + if(fs.existsSync("./package-lock.json")) { + var packageLock = JSON.parse(fs.readFileSync("./package-lock.json")); + + if(![1, 2].includes(packageLock.lockfileVersion)) { + process.stderr.write("Sorry, I only understand lock file versions 1 and 2!\n"); + process.exit(1); + } + + if(packageLock.dependencies !== undefined) { + augmentDependencies(".", packageLock.dependencies); + } + } + ''; + }; + + # Reconstructs a package-lock file from the node_modules/ folder structure and package.json files with dummy sha1 hashes + reconstructPackageLock = writeTextFile { + name = "reconstructpackagelock.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + var packageObj = JSON.parse(fs.readFileSync("package.json")); + + var lockObj = { + name: packageObj.name, + version: packageObj.version, + lockfileVersion: 2, + requires: true, + packages: { + "": { + name: packageObj.name, + version: packageObj.version, + license: packageObj.license, + bin: packageObj.bin, + dependencies: packageObj.dependencies, + engines: packageObj.engines, + optionalDependencies: packageObj.optionalDependencies + } + }, + dependencies: {} + }; + + function augmentPackageJSON(filePath, packages, dependencies) { + var packageJSON = path.join(filePath, "package.json"); + if(fs.existsSync(packageJSON)) { + var packageObj = JSON.parse(fs.readFileSync(packageJSON)); + packages[filePath] = { + version: packageObj.version, + integrity: "sha1-000000000000000000000000000=", + dependencies: packageObj.dependencies, + engines: packageObj.engines, + optionalDependencies: packageObj.optionalDependencies + }; + dependencies[packageObj.name] = { + version: packageObj.version, + integrity: "sha1-000000000000000000000000000=", + dependencies: {} + }; + processDependencies(path.join(filePath, "node_modules"), packages, dependencies[packageObj.name].dependencies); + } + } + + function processDependencies(dir, packages, dependencies) { + if(fs.existsSync(dir)) { + var files = fs.readdirSync(dir); + + files.forEach(function(entry) { + var filePath = path.join(dir, entry); + var stats = fs.statSync(filePath); + + if(stats.isDirectory()) { + if(entry.substr(0, 1) == "@") { + // When we encounter a namespace folder, augment all packages belonging to the scope + var pkgFiles = fs.readdirSync(filePath); + + pkgFiles.forEach(function(entry) { + if(stats.isDirectory()) { + var pkgFilePath = path.join(filePath, entry); + augmentPackageJSON(pkgFilePath, packages, dependencies); + } + }); + } else { + augmentPackageJSON(filePath, packages, dependencies); + } + } + }); + } + } + + processDependencies("node_modules", lockObj.packages, lockObj.dependencies); + + fs.writeFileSync("package-lock.json", JSON.stringify(lockObj, null, 2)); + ''; + }; + + # Script that links bins defined in package.json to the node_modules bin directory + # NPM does not do this for top-level packages itself anymore as of v7 + linkBinsScript = writeTextFile { + name = "linkbins.js"; + text = '' + var fs = require('fs'); + var path = require('path'); + + var packageObj = JSON.parse(fs.readFileSync("package.json")); + + var nodeModules = Array(packageObj.name.split("/").length).fill("..").join(path.sep); + + if(packageObj.bin !== undefined) { + fs.mkdirSync(path.join(nodeModules, ".bin")) + + if(typeof packageObj.bin == "object") { + Object.keys(packageObj.bin).forEach(function(exe) { + if(fs.existsSync(packageObj.bin[exe])) { + console.log("linking bin '" + exe + "'"); + fs.symlinkSync( + path.join("..", packageObj.name, packageObj.bin[exe]), + path.join(nodeModules, ".bin", exe) + ); + } + else { + console.log("skipping non-existent bin '" + exe + "'"); + } + }) + } + else { + if(fs.existsSync(packageObj.bin)) { + console.log("linking bin '" + packageObj.bin + "'"); + fs.symlinkSync( + path.join("..", packageObj.name, packageObj.bin), + path.join(nodeModules, ".bin", packageObj.name.split("/").pop()) + ); + } + else { + console.log("skipping non-existent bin '" + packageObj.bin + "'"); + } + } + } + else if(packageObj.directories !== undefined && packageObj.directories.bin !== undefined) { + fs.mkdirSync(path.join(nodeModules, ".bin")) + + fs.readdirSync(packageObj.directories.bin).forEach(function(exe) { + if(fs.existsSync(path.join(packageObj.directories.bin, exe))) { + console.log("linking bin '" + exe + "'"); + fs.symlinkSync( + path.join("..", packageObj.name, packageObj.directories.bin, exe), + path.join(nodeModules, ".bin", exe) + ); + } + else { + console.log("skipping non-existent bin '" + exe + "'"); + } + }) + } + ''; + }; + + prepareAndInvokeNPM = {packageName, bypassCache, reconstructLock, npmFlags, production}: + let + forceOfflineFlag = if bypassCache then "--offline" else "--registry http://www.example.com"; + in + '' + # Pinpoint the versions of all dependencies to the ones that are actually being used + echo "pinpointing versions of dependencies..." + source $pinpointDependenciesScriptPath + + # Patch the shebangs of the bundled modules to prevent them from + # calling executables outside the Nix store as much as possible + patchShebangs . + + # Deploy the Node.js package by running npm install. Since the + # dependencies have been provided already by ourselves, it should not + # attempt to install them again, which is good, because we want to make + # it Nix's responsibility. If it needs to install any dependencies + # anyway (e.g. because the dependency parameters are + # incomplete/incorrect), it fails. + # + # The other responsibilities of NPM are kept -- version checks, build + # steps, postprocessing etc. + + export HOME=$TMPDIR + cd "${packageName}" + runHook preRebuild + + ${lib.optionalString bypassCache '' + ${lib.optionalString reconstructLock '' + if [ -f package-lock.json ] + then + echo "WARNING: Reconstruct lock option enabled, but a lock file already exists!" + echo "This will most likely result in version mismatches! We will remove the lock file and regenerate it!" + rm package-lock.json + else + echo "No package-lock.json file found, reconstructing..." + fi + + node ${reconstructPackageLock} + ''} + + node ${addIntegrityFieldsScript} + ''} + + npm ${forceOfflineFlag} --nodedir=${nodeSources} ${npmFlags} ${lib.optionalString production "--production"} rebuild + + runHook postRebuild + + if [ "''${dontNpmInstall-}" != "1" ] + then + # NPM tries to download packages even when they already exist if npm-shrinkwrap is used. + rm -f npm-shrinkwrap.json + + npm ${forceOfflineFlag} --nodedir=${nodeSources} --no-bin-links --ignore-scripts ${npmFlags} ${lib.optionalString production "--production"} install + fi + + # Link executables defined in package.json + node ${linkBinsScript} + ''; + + # Builds and composes an NPM package including all its dependencies + buildNodePackage = + { name + , packageName + , version ? null + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , preRebuild ? "" + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , meta ? {} + , ... }@args: + + let + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "preRebuild" "unpackPhase" "buildPhase" "meta" ]; + in + stdenv.mkDerivation ({ + name = "${name}${if version == null then "" else "-${version}"}"; + buildInputs = [ tarWrapper python nodejs ] + ++ lib.optional (stdenv.isLinux) utillinux + ++ lib.optional (stdenv.isDarwin) libtool + ++ buildInputs; + + inherit nodejs; + + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall preRebuild unpackPhase buildPhase; + + compositionScript = composePackage args; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "compositionScript" "pinpointDependenciesScript" ]; + + installPhase = '' + source ${installPackage} + + # Create and enter a root node_modules/ folder + mkdir -p $out/lib/node_modules + cd $out/lib/node_modules + + # Compose the package and all its dependencies + source $compositionScriptPath + + ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} + + # Create symlink to the deployed executable folder, if applicable + if [ -d "$out/lib/node_modules/.bin" ] + then + ln -s $out/lib/node_modules/.bin $out/bin + + # Fixup all executables + ls $out/bin/* | while read i + do + file="$(readlink -f "$i")" + chmod u+rwx "$file" + if isScript "$file" + then + sed -i 's/\r$//' "$file" # convert crlf to lf + fi + done + fi + + # Create symlinks to the deployed manual page folders, if applicable + if [ -d "$out/lib/node_modules/${packageName}/man" ] + then + mkdir -p $out/share + for dir in "$out/lib/node_modules/${packageName}/man/"* + do + mkdir -p $out/share/man/$(basename "$dir") + for page in "$dir"/* + do + ln -s $page $out/share/man/$(basename "$dir") + done + done + fi + + # Run post install hook, if provided + runHook postInstall + ''; + + meta = { + # default to Node.js' platforms + platforms = nodejs.meta.platforms; + } // meta; + } // extraArgs); + + # Builds a node environment (a node_modules folder and a set of binaries) + buildNodeDependencies = + { name + , packageName + , version ? null + , src + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , ... }@args: + + let + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" ]; + in + stdenv.mkDerivation ({ + name = "node-dependencies-${name}${if version == null then "" else "-${version}"}"; + + buildInputs = [ tarWrapper python nodejs ] + ++ lib.optional (stdenv.isLinux) utillinux + ++ lib.optional (stdenv.isDarwin) libtool + ++ buildInputs; + + inherit dontStrip; # Stripping may fail a build for some package deployments + inherit dontNpmInstall unpackPhase buildPhase; + + includeScript = includeDependencies { inherit dependencies; }; + pinpointDependenciesScript = pinpointDependenciesOfPackage args; + + passAsFile = [ "includeScript" "pinpointDependenciesScript" ]; + + installPhase = '' + source ${installPackage} + + mkdir -p $out/${packageName} + cd $out/${packageName} + + source $includeScriptPath + + # Create fake package.json to make the npm commands work properly + cp ${src}/package.json . + chmod 644 package.json + ${lib.optionalString bypassCache '' + if [ -f ${src}/package-lock.json ] + then + cp ${src}/package-lock.json . + chmod 644 package-lock.json + fi + ''} + + # Go to the parent folder to make sure that all packages are pinpointed + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + + ${prepareAndInvokeNPM { inherit packageName bypassCache reconstructLock npmFlags production; }} + + # Expose the executables that were installed + cd .. + ${lib.optionalString (builtins.substring 0 1 packageName == "@") "cd .."} + + mv ${packageName} lib + ln -s $out/lib/node_modules/.bin $out/bin + ''; + } // extraArgs); + + # Builds a development shell + buildNodeShell = + { name + , packageName + , version ? null + , src + , dependencies ? [] + , buildInputs ? [] + , production ? true + , npmFlags ? "" + , dontNpmInstall ? false + , bypassCache ? false + , reconstructLock ? false + , dontStrip ? true + , unpackPhase ? "true" + , buildPhase ? "true" + , ... }@args: + + let + nodeDependencies = buildNodeDependencies args; + extraArgs = removeAttrs args [ "name" "dependencies" "buildInputs" "dontStrip" "dontNpmInstall" "unpackPhase" "buildPhase" ]; + in + stdenv.mkDerivation ({ + name = "node-shell-${name}${if version == null then "" else "-${version}"}"; + + buildInputs = [ python nodejs ] ++ lib.optional (stdenv.isLinux) utillinux ++ buildInputs; + buildCommand = '' + mkdir -p $out/bin + cat > $out/bin/shell <=14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", + "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/openapi-typescript": { + "version": "6.7.6", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.6.tgz", + "integrity": "sha512-c/hfooPx+RBIOPM09GSxABOZhYPblDoyaGhqBkD/59vtpN21jEuWKDlM0KYTvqJVlSYjKs0tBcIdeXKChlSPtw==", + "dependencies": { + "ansi-colors": "^4.1.3", + "fast-glob": "^3.3.2", + "js-yaml": "^4.1.0", + "supports-color": "^9.4.0", + "undici": "^5.28.4", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/undici": { + "version": "5.28.4", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz", + "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + } + } +} diff --git a/libs/cli-kontrol-api/nix/package.json b/libs/cli-kontrol-api/nix/package.json new file mode 100644 index 00000000..d83d9126 --- /dev/null +++ b/libs/cli-kontrol-api/nix/package.json @@ -0,0 +1,7 @@ +{ + "name": "cli-kontrol-api-tools", + "version": "1.0.0", + "dependencies": { + "openapi-typescript": "^6.7.6" + } +} diff --git a/libs/cli-kontrol-api/scripts/build.sh b/libs/cli-kontrol-api/scripts/build.sh new file mode 100755 index 00000000..fe634b47 --- /dev/null +++ b/libs/cli-kontrol-api/scripts/build.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -euo pipefail # Bash "strict mode" +script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +openapi_root_dirpath="$(dirname "${script_dirpath}")" +api_root_dirpath="$(dirname "${openapi_root_dirpath}")" + +echo "Generating data models for REST API " +oapi-codegen --config="$openapi_root_dirpath/generators/api_types.cfg.yaml" "$openapi_root_dirpath/specs/api.yaml" + +echo "Generating server code for REST API " +oapi-codegen --config="$openapi_root_dirpath/generators/go_server.cfg.yaml" "$openapi_root_dirpath/specs/api.yaml" + +echo "Generating Go client code for REST API " +oapi-codegen --config="$openapi_root_dirpath/generators/go_client.cfg.yaml" "$openapi_root_dirpath/specs/api.yaml" + +echo "Generating Typescript client code for REST API " +openapi-typescript "$openapi_root_dirpath/specs/api.yaml" -o "$openapi_root_dirpath/api/typescript/client/types.d.ts" diff --git a/libs/cli-kontrol-api/shell.nix b/libs/cli-kontrol-api/shell.nix new file mode 100644 index 00000000..12389ed8 --- /dev/null +++ b/libs/cli-kontrol-api/shell.nix @@ -0,0 +1,25 @@ +{pkgs}: let + goEnv = pkgs.mkGoEnv {pwd = ./.;}; + node-devtools = import ./nix/. { + inherit pkgs; + nodejs = pkgs.nodejs_20; + }; +in + pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + goEnv + + goreleaser + go + gopls + golangci-lint + delve + enumer + gomod2nix + + oapi-codegen + nodejs + node2nix + node-devtools.nodeDependencies + ]; + } diff --git a/libs/cli-kontrol-api/specs/api.yaml b/libs/cli-kontrol-api/specs/api.yaml new file mode 100644 index 00000000..3658debb --- /dev/null +++ b/libs/cli-kontrol-api/specs/api.yaml @@ -0,0 +1,142 @@ +openapi: 3.0.0 +info: + title: CLI/Kontrol API + version: 1.0.0 +paths: + /flow/create: + post: + requestBody: + description: Create a dev flow + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DevFlowSpec" + responses: + "200": + description: Dev flow creation status + content: + application/json: + schema: + type: string + /flow/delete: + post: + requestBody: + description: Delete dev flow (revert back to prod only) + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ProdFlowSpec" + responses: + "200": + description: Dev flow creation status + content: + application/json: + schema: + type: string + /deploy: + post: + requestBody: + description: Deploy a prod only cluster + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ProdFlowSpec" + responses: + "200": + description: Dev flow creation status + content: + application/json: + schema: + type: string + /topology: + get: + parameters: + - in: query + name: namespace + schema: + type: string + required: false + description: The namespace for which to retrieve the topology + responses: + "200": + description: Topology information + content: + application/json: + schema: + $ref: "#/components/schemas/ClusterTopology" + +components: + schemas: + ProdFlowSpec: + type: object + properties: + docker-compose: + type: array + items: + x-go-type: compose.ServiceConfig + x-go-type-import: + path: github.com/compose-spec/compose-go/types + name: compose + + DevFlowSpec: + type: object + properties: + image-locator: + type: string + example: backend-a:latest + service-name: + type: string + example: backend-service-a + docker-compose: + type: array + items: + x-go-type: compose.ServiceConfig + x-go-type-import: + path: github.com/compose-spec/compose-go/types + name: compose + + Node: + type: object + properties: + id: + type: string + description: Unique identifier for the node. + label: + type: string + description: Label for the node. + required: + - id + + Edge: + type: object + properties: + source: + type: string + description: The identifier of the source node of the edge. + target: + type: string + description: The identifier of the target node of the edge. + label: + type: string + description: Label for the edge. + required: + - source + - target + + ClusterTopology: + type: object + properties: + nodes: + type: array + items: + $ref: "#/components/schemas/Node" + edges: + type: array + items: + $ref: "#/components/schemas/Edge" + required: + - nodes + - edges diff --git a/libs/manager-kontrol-api/README.md b/libs/manager-kontrol-api/README.md new file mode 100644 index 00000000..23dd2cc0 --- /dev/null +++ b/libs/manager-kontrol-api/README.md @@ -0,0 +1,9 @@ +# Manager-Kontrol API + +The OpenAPI specs are located in the `specs` directory. Whenever the specs are updated, the REST API bindings should be regenerated. This can be done by running the following command: + +```bash +# Make sure your are inside a nix shell (nix develop) +./scripts/build.sh +''' +``` diff --git a/libs/manager-kontrol-api/api/golang/client/client.gen.go b/libs/manager-kontrol-api/api/golang/client/client.gen.go new file mode 100644 index 00000000..a2833afa --- /dev/null +++ b/libs/manager-kontrol-api/api/golang/client/client.gen.go @@ -0,0 +1,267 @@ +// Package client provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version 2.1.0 DO NOT EDIT. +package client + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + + . "github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api/api/golang/types" + "github.com/oapi-codegen/runtime" +) + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // GetClusterResources request + GetClusterResources(ctx context.Context, params *GetClusterResourcesParams, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) GetClusterResources(ctx context.Context, params *GetClusterResourcesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetClusterResourcesRequest(c.Server, params) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewGetClusterResourcesRequest generates requests for GetClusterResources +func NewGetClusterResourcesRequest(server string, params *GetClusterResourcesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/cluster-resources") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.Namespace != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "namespace", runtime.ParamLocationQuery, *params.Namespace); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { + for _, r := range c.RequestEditors { + if err := r(ctx, req); err != nil { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } + } + return nil +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) + if err != nil { + return nil, err + } + return &ClientWithResponses{client}, nil +} + +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // GetClusterResourcesWithResponse request + GetClusterResourcesWithResponse(ctx context.Context, params *GetClusterResourcesParams, reqEditors ...RequestEditorFn) (*GetClusterResourcesResponse, error) +} + +type GetClusterResourcesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ClusterResources + JSONDefault *NotOk +} + +// Status returns HTTPResponse.Status +func (r GetClusterResourcesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetClusterResourcesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// GetClusterResourcesWithResponse request returning *GetClusterResourcesResponse +func (c *ClientWithResponses) GetClusterResourcesWithResponse(ctx context.Context, params *GetClusterResourcesParams, reqEditors ...RequestEditorFn) (*GetClusterResourcesResponse, error) { + rsp, err := c.GetClusterResources(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetClusterResourcesResponse(rsp) +} + +// ParseGetClusterResourcesResponse parses an HTTP response from a GetClusterResourcesWithResponse call +func ParseGetClusterResourcesResponse(rsp *http.Response) (*GetClusterResourcesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetClusterResourcesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ClusterResources + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && true: + var dest NotOk + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSONDefault = &dest + + } + + return response, nil +} diff --git a/libs/manager-kontrol-api/api/golang/server/server.gen.go b/libs/manager-kontrol-api/api/golang/server/server.gen.go new file mode 100644 index 00000000..3f56219d --- /dev/null +++ b/libs/manager-kontrol-api/api/golang/server/server.gen.go @@ -0,0 +1,249 @@ +// Package server provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version 2.1.0 DO NOT EDIT. +package server + +import ( + "bytes" + "compress/gzip" + "context" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "net/url" + "path" + "strings" + + "github.com/getkin/kin-openapi/openapi3" + . "github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api/api/golang/types" + "github.com/labstack/echo/v4" + "github.com/oapi-codegen/runtime" + strictecho "github.com/oapi-codegen/runtime/strictmiddleware/echo" +) + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Cluster resource definition + // (GET /cluster-resources) + GetClusterResources(ctx echo.Context, params GetClusterResourcesParams) error +} + +// ServerInterfaceWrapper converts echo contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface +} + +// GetClusterResources converts echo context to params. +func (w *ServerInterfaceWrapper) GetClusterResources(ctx echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetClusterResourcesParams + // ------------- Optional query parameter "namespace" ------------- + + err = runtime.BindQueryParameter("form", true, false, "namespace", ctx.QueryParams(), ¶ms.Namespace) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter namespace: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetClusterResources(ctx, params) + return err +} + +// This is a simple interface which specifies echo.Route addition functions which +// are present on both echo.Echo and echo.Group, since we want to allow using +// either of them for path registration +type EchoRouter interface { + CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route + TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route +} + +// RegisterHandlers adds each server route to the EchoRouter. +func RegisterHandlers(router EchoRouter, si ServerInterface) { + RegisterHandlersWithBaseURL(router, si, "") +} + +// Registers handlers, and prepends BaseURL to the paths, so that the paths +// can be served under a prefix. +func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL string) { + + wrapper := ServerInterfaceWrapper{ + Handler: si, + } + + router.GET(baseURL+"/cluster-resources", wrapper.GetClusterResources) + +} + +type NotOkJSONResponse ResponseInfo + +type GetClusterResourcesRequestObject struct { + Params GetClusterResourcesParams +} + +type GetClusterResourcesResponseObject interface { + VisitGetClusterResourcesResponse(w http.ResponseWriter) error +} + +type GetClusterResources200JSONResponse ClusterResources + +func (response GetClusterResources200JSONResponse) VisitGetClusterResourcesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetClusterResourcesdefaultJSONResponse struct { + Body ResponseInfo + StatusCode int +} + +func (response GetClusterResourcesdefaultJSONResponse) VisitGetClusterResourcesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(response.StatusCode) + + return json.NewEncoder(w).Encode(response.Body) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // Cluster resource definition + // (GET /cluster-resources) + GetClusterResources(ctx context.Context, request GetClusterResourcesRequestObject) (GetClusterResourcesResponseObject, error) +} + +type StrictHandlerFunc = strictecho.StrictEchoHandlerFunc +type StrictMiddlewareFunc = strictecho.StrictEchoMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// GetClusterResources operation middleware +func (sh *strictHandler) GetClusterResources(ctx echo.Context, params GetClusterResourcesParams) error { + var request GetClusterResourcesRequestObject + + request.Params = params + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.GetClusterResources(ctx.Request().Context(), request.(GetClusterResourcesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetClusterResources") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(GetClusterResourcesResponseObject); ok { + return validResponse.VisitGetClusterResourcesResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("unexpected response type: %T", response) + } + return nil +} + +// Base64 encoded, gzipped, json marshaled Swagger object +var swaggerSpec = []string{ + + "H4sIAAAAAAAC/7yVQW/bOgzHv4rB945OnLSXB9+K1/eKoFg6pN12KIpCkxlHjS2qFO02KPLdB9mOkzZN", + "1sO2UxSR+vMn0iJfQFPpyKIVD+kLMHpH1mPzZ0pytQwLTVbQSlgq5wqjlRiyyYMnG/a8XmCpwupvxjmk", + "8FeyVU1aq09mnfTEzgnW63UMGXrNxgUtSOGLxWeHWjCLkJkYgkt3OGj/W1RekGfoqWLdEjomhyym/Zeh", + "K2hVbu5iBMtm8TzIaSArh5AGfl+Ph+e9K8Rb+8CUjri5p1Xl1h1icEoWkMLyHz80lChnkmBK6nGDuRFn", + "VitobybGNlm656rAwzz1WBVuoU6H59sjs6rA41ibU1sw48VQQNOFQSuDnBK3zAOoTyzKE/HS2DzpD75H", + "nSvBJ7U6iHjR2f8AmkeujT6SN02M9Xh43fodR2p93y1iMB0qYm1YKlXc/5SlT9DX9sSHoH5HCbcb9P0B", + "tYRbvHp2e09GU4bhd05cKoEUKmPl9AR6IWMFc+SgVKL3Km/cO6MXNjaHPuzHGsBN8A2sjI+VYcwgvW0F", + "tjHiluzuyIVuupBoqzIo/DebXc0ghsn0/yuI4dvZbDqZXuxIbGhDaNNlQ4wUwfZJWZUjJ5cFVdngkqww", + "FdHZ5wnEUCP7tkWNh6PhKDCQQ6ucgRROm622hk1GE932qQHvNqocm9qH1DdPfJJBChcoe00tKLEqUZA9", + "pLcvb5rkzQKj8AF5pzRGc+LoaWH0IhKKGIUN1hjJAqMOIuIdYRMEHivk8IK7r7DXgninjb9N2F38ejCc", + "jEa/bCzsZeCd0XBdaY3ez6si2nC0XXauqkIOReiRk3aQNX2lKkvFK0g346TPUJTh3FjTRIxBVB7SD/vF", + "vFuv1+sfAQAA//+4aAzDNwcAAA==", +} + +// GetSwagger returns the content of the embedded swagger specification file +// or error if failed to decode +func decodeSpec() ([]byte, error) { + zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) + if err != nil { + return nil, fmt.Errorf("error base64 decoding spec: %w", err) + } + zr, err := gzip.NewReader(bytes.NewReader(zipped)) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + var buf bytes.Buffer + _, err = buf.ReadFrom(zr) + if err != nil { + return nil, fmt.Errorf("error decompressing spec: %w", err) + } + + return buf.Bytes(), nil +} + +var rawSpec = decodeSpecCached() + +// a naive cached of a decoded swagger spec +func decodeSpecCached() func() ([]byte, error) { + data, err := decodeSpec() + return func() ([]byte, error) { + return data, err + } +} + +// Constructs a synthetic filesystem for resolving external references when loading openapi specifications. +func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { + res := make(map[string]func() ([]byte, error)) + if len(pathToFile) > 0 { + res[pathToFile] = rawSpec + } + + return res +} + +// GetSwagger returns the Swagger specification corresponding to the generated code +// in this file. The external references of Swagger specification are resolved. +// The logic of resolving external references is tightly connected to "import-mapping" feature. +// Externally referenced files must be embedded in the corresponding golang packages. +// Urls can be supported but this task was out of the scope. +func GetSwagger() (swagger *openapi3.T, err error) { + resolvePath := PathToRawSpec("") + + loader := openapi3.NewLoader() + loader.IsExternalRefsAllowed = true + loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { + pathToFile := url.String() + pathToFile = path.Clean(pathToFile) + getSpec, ok := resolvePath[pathToFile] + if !ok { + err1 := fmt.Errorf("path not found: %s", pathToFile) + return nil, err1 + } + return getSpec() + } + var specData []byte + specData, err = rawSpec() + if err != nil { + return + } + swagger, err = loader.LoadFromData(specData) + if err != nil { + return + } + return +} diff --git a/libs/manager-kontrol-api/api/golang/types/types.gen.go b/libs/manager-kontrol-api/api/golang/types/types.gen.go new file mode 100644 index 00000000..d36298c6 --- /dev/null +++ b/libs/manager-kontrol-api/api/golang/types/types.gen.go @@ -0,0 +1,45 @@ +// Package types provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/deepmap/oapi-codegen/v2 version 2.1.0 DO NOT EDIT. +package types + +import ( + v1alpha3 "istio.io/client-go/pkg/apis/networking/v1alpha3" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" +) + +// Defines values for ResponseType. +const ( + ERROR ResponseType = "ERROR" + INFO ResponseType = "INFO" + WARNING ResponseType = "WARNING" +) + +// ClusterResources defines model for ClusterResources. +type ClusterResources struct { + Deployments *[]appsv1.Deployment `json:"deployments,omitempty"` + DestinationRules *[]v1alpha3.DestinationRule `json:"destination_rules,omitempty"` + Gateway *v1alpha3.Gateway `json:"gateway,omitempty"` + Services *[]corev1.Service `json:"services,omitempty"` + VirtualServices *[]v1alpha3.VirtualService `json:"virtual_services,omitempty"` +} + +// ResponseInfo defines model for ResponseInfo. +type ResponseInfo struct { + Code uint32 `json:"code"` + Message string `json:"message"` + Type ResponseType `json:"type"` +} + +// ResponseType defines model for ResponseType. +type ResponseType string + +// NotOk defines model for NotOk. +type NotOk = ResponseInfo + +// GetClusterResourcesParams defines parameters for GetClusterResources. +type GetClusterResourcesParams struct { + // Namespace The namespace for which to retrieve the cluster resources + Namespace *string `form:"namespace,omitempty" json:"namespace,omitempty"` +} diff --git a/libs/manager-kontrol-api/api/typescript/client/types.d.ts b/libs/manager-kontrol-api/api/typescript/client/types.d.ts new file mode 100644 index 00000000..25e64bae --- /dev/null +++ b/libs/manager-kontrol-api/api/typescript/client/types.d.ts @@ -0,0 +1,68 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + + +export interface paths { + "/cluster-resources": { + /** Cluster resource definition */ + get: { + parameters: { + query?: { + /** @description The namespace for which to retrieve the cluster resources */ + namespace?: string; + }; + }; + responses: { + /** @description Successful response */ + 200: { + content: { + "application/json": components["schemas"]["ClusterResources"]; + }; + }; + default: components["responses"]["NotOk"]; + }; + }; + }; +} + +export type webhooks = Record; + +export interface components { + schemas: { + ResponseInfo: { + type: components["schemas"]["ResponseType"]; + message: string; + /** Format: uint32 */ + code: number; + }; + /** @enum {string} */ + ResponseType: "ERROR" | "INFO" | "WARNING"; + ClusterResources: { + services?: unknown[]; + deployments?: unknown[]; + virtual_services?: unknown[]; + destination_rules?: unknown[]; + gateway?: unknown; + }; + }; + responses: { + /** @description Unexpected error */ + NotOk: { + content: { + "application/json": components["schemas"]["ResponseInfo"]; + }; + }; + }; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} + +export type $defs = Record; + +export type external = Record; + +export type operations = Record; diff --git a/libs/manager-kontrol-api/generators/api_types.cfg.yaml b/libs/manager-kontrol-api/generators/api_types.cfg.yaml new file mode 100644 index 00000000..2473c54b --- /dev/null +++ b/libs/manager-kontrol-api/generators/api_types.cfg.yaml @@ -0,0 +1,4 @@ +package: types +generate: + models: true +output: api/golang/types/types.gen.go diff --git a/libs/manager-kontrol-api/generators/go_client.cfg.yaml b/libs/manager-kontrol-api/generators/go_client.cfg.yaml new file mode 100644 index 00000000..b707dae3 --- /dev/null +++ b/libs/manager-kontrol-api/generators/go_client.cfg.yaml @@ -0,0 +1,7 @@ +package: client +generate: + client: true +additional-imports: + - package: github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api/api/golang/types + alias: . +output: api/golang/client/client.gen.go diff --git a/libs/manager-kontrol-api/generators/go_server.cfg.yaml b/libs/manager-kontrol-api/generators/go_server.cfg.yaml new file mode 100644 index 00000000..72db530d --- /dev/null +++ b/libs/manager-kontrol-api/generators/go_server.cfg.yaml @@ -0,0 +1,9 @@ +package: server +generate: + embedded-spec: true + echo-server: true + strict-server: true +additional-imports: + - package: github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api/api/golang/types + alias: . +output: api/golang/server/server.gen.go diff --git a/libs/manager-kontrol-api/go.mod b/libs/manager-kontrol-api/go.mod new file mode 100644 index 00000000..1a278853 --- /dev/null +++ b/libs/manager-kontrol-api/go.mod @@ -0,0 +1,52 @@ +module github.com/kurtosis-tech/kardinal/libs/manager-kontrol-api + +go 1.22.0 + +toolchain go1.22.3 + +require ( + github.com/getkin/kin-openapi v0.125.0 + github.com/labstack/echo/v4 v4.12.0 + github.com/oapi-codegen/runtime v1.1.1 + istio.io/client-go v1.22.1 + k8s.io/api v0.30.2 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-openapi/jsonpointer v0.20.2 // indirect + github.com/go-openapi/swag v0.22.8 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/invopop/yaml v0.2.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect + github.com/perimeterx/marshmallow v1.1.5 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + istio.io/api v1.22.1-0.20240524024004-b6815be0740d // indirect + k8s.io/apimachinery v0.30.2 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect +) diff --git a/libs/manager-kontrol-api/go.sum b/libs/manager-kontrol-api/go.sum new file mode 100644 index 00000000..963bbb47 --- /dev/null +++ b/libs/manager-kontrol-api/go.sum @@ -0,0 +1,151 @@ +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/getkin/kin-openapi v0.125.0 h1:jyQCyf2qXS1qvs2U00xQzkGCqYPhEhZDmSmVt65fXno= +github.com/getkin/kin-openapi v0.125.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q= +github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs= +github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw= +github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI= +github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= +github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY= +github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0= +github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= +github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= +github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= +github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +istio.io/api v1.22.1-0.20240524024004-b6815be0740d h1:2GncSQ55NOr91NYPmi0jqhVM7z7/xswJsD96dQMkN38= +istio.io/api v1.22.1-0.20240524024004-b6815be0740d/go.mod h1:S3l8LWqNYS9yT+d4bH+jqzH2lMencPkW7SKM1Cu9EyM= +istio.io/client-go v1.22.1 h1:78BUMxytD0muwpwHdcA9qTOTJXN0jib0mXmNLdXxj0c= +istio.io/client-go v1.22.1/go.mod h1:Z2QE9uMt6tDVyrmiLfLVhutbqtfUkPJ7A5Uw/p6gNFo= +k8s.io/api v0.30.2 h1:+ZhRj+28QT4UOH+BKznu4CBgPWgkXO7XAvMcMl0qKvI= +k8s.io/api v0.30.2/go.mod h1:ULg5g9JvOev2dG0u2hig4Z7tQ2hHIuS+m8MNZ+X6EmI= +k8s.io/apimachinery v0.30.2 h1:fEMcnBj6qkzzPGSVsAZtQThU62SmQ4ZymlXRC5yFSCg= +k8s.io/apimachinery v0.30.2/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/libs/manager-kontrol-api/scripts/build.sh b/libs/manager-kontrol-api/scripts/build.sh new file mode 100755 index 00000000..fe634b47 --- /dev/null +++ b/libs/manager-kontrol-api/scripts/build.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -euo pipefail # Bash "strict mode" +script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +openapi_root_dirpath="$(dirname "${script_dirpath}")" +api_root_dirpath="$(dirname "${openapi_root_dirpath}")" + +echo "Generating data models for REST API " +oapi-codegen --config="$openapi_root_dirpath/generators/api_types.cfg.yaml" "$openapi_root_dirpath/specs/api.yaml" + +echo "Generating server code for REST API " +oapi-codegen --config="$openapi_root_dirpath/generators/go_server.cfg.yaml" "$openapi_root_dirpath/specs/api.yaml" + +echo "Generating Go client code for REST API " +oapi-codegen --config="$openapi_root_dirpath/generators/go_client.cfg.yaml" "$openapi_root_dirpath/specs/api.yaml" + +echo "Generating Typescript client code for REST API " +openapi-typescript "$openapi_root_dirpath/specs/api.yaml" -o "$openapi_root_dirpath/api/typescript/client/types.d.ts" diff --git a/libs/manager-kontrol-api/shell.nix b/libs/manager-kontrol-api/shell.nix new file mode 100644 index 00000000..57d47471 --- /dev/null +++ b/libs/manager-kontrol-api/shell.nix @@ -0,0 +1,25 @@ +{pkgs ? import {} }: let + goEnv = pkgs.mkGoEnv {pwd = ./.;}; + node-devtools = import ./nix/. { + inherit pkgs; + nodejs = pkgs.nodejs_20; + }; +in + pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + goEnv + + goreleaser + go + gopls + golangci-lint + delve + enumer + gomod2nix + + oapi-codegen + nodejs + node2nix + node-devtools.nodeDependencies + ]; + } diff --git a/libs/manager-kontrol-api/specs/api.yaml b/libs/manager-kontrol-api/specs/api.yaml new file mode 100644 index 00000000..98900191 --- /dev/null +++ b/libs/manager-kontrol-api/specs/api.yaml @@ -0,0 +1,95 @@ +openapi: 3.0.0 +info: + title: Manager/Kontrol API + version: 1.0.0 +paths: + /cluster-resources: + get: + tags: + - cluster-resources + summary: Cluster resource definition + parameters: + - in: query + name: namespace + schema: + type: string + required: false + description: The namespace for which to retrieve the cluster resources + responses: + default: + $ref: "#/components/responses/NotOk" + "200": + description: Successful response + content: + application/json: + schema: + $ref: "#/components/schemas/ClusterResources" + +components: + responses: + NotOk: + description: Unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseInfo" + required: true + schemas: + ResponseInfo: + type: object + properties: + type: + $ref: "#/components/schemas/ResponseType" + message: + type: string + code: + type: integer + format: uint32 + required: + - type + - message + - code + + ResponseType: + type: string + enum: + - ERROR + - INFO + - WARNING + + ClusterResources: + type: object + properties: + services: + type: array + items: + x-go-type: corev1.Service + x-go-type-import: + path: k8s.io/api/core/v1 + name: corev1 + deployments: + type: array + items: + x-go-type: appsv1.Deployment + x-go-type-import: + path: k8s.io/api/apps/v1 + name: appsv1 + virtual_services: + type: array + items: + x-go-type: v1alpha3.VirtualService + x-go-type-import: + path: istio.io/client-go/pkg/apis/networking/v1alpha3 + name: v1alpha3 + destination_rules: + type: array + items: + x-go-type: v1alpha3.DestinationRule + x-go-type-import: + path: istio.io/client-go/pkg/apis/networking/v1alpha3 + name: v1alpha3 + gateway: + x-go-type: v1alpha3.Gateway + x-go-type-import: + path: istio.io/client-go/pkg/apis/networking/v1alpha3 + name: v1alpha3 diff --git a/scripts/go-tidy-all.sh b/scripts/go-tidy-all.sh new file mode 100755 index 00000000..b2473c34 --- /dev/null +++ b/scripts/go-tidy-all.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +set -euo pipefail # Bash "strict mode" +script_dirpath="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +root_dirpath="$(dirname "${script_dirpath}")" + +find $root_dirpath -type f -name 'go.mod' -exec sh -c 'dir=$(dirname "{}") && cd "$dir" && echo "$dir" && go mod tidy' \; diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..d391d9da --- /dev/null +++ b/shell.nix @@ -0,0 +1,51 @@ +{pkgs}: let + mergeShells = shells: + pkgs.mkShell { + shellHook = builtins.concatStringsSep "\n" (map (s: s.shellHook or "") shells); + buildInputs = builtins.concatLists (map (s: s.buildInputs or []) shells); + nativeBuildInputs = builtins.concatLists (map (s: s.nativeBuildInputs or []) shells); + paths = builtins.concatLists (map (s: s.paths or []) shells); + }; + manager_shell = pkgs.callPackage ./kardinal-manager/shell.nix {inherit pkgs;}; + cli_shell = pkgs.callPackage ./kardinal-cli/shell.nix {inherit pkgs;}; + cli_kontrol_api_shell = pkgs.callPackage ./libs/cli-kontrol-api/shell.nix {inherit pkgs;}; + kardinal_shell = with pkgs; + pkgs.mkShell { + buildInputs = [k3d kubectl kustomize argo-rollouts kubernetes-helm minikube istioctl tilt reflex]; + shellHook = '' + source <(kubectl completion bash) + source <(kubectl-argo-rollouts completion bash) + source <(minikube completion bash) + printf '\u001b[31m + + ::::: + ::::::: + :: ::: + ::: :: + :: ::- ::: + ::: ::: + ::: ::: ::: + ::: :: :: + ::: :: ::: + ::: ::: :: + ::: :: :: + :::: :::: :: + :::: :::: ::: + :::::::::::::: :::: + :::::: + ::::::::::::::::::::: + :::::: + ::::: + ::: + + + + \u001b[0m + Starting Kardinal dev shell. + \e[32m + \e[0m + ' + ''; + }; +in + mergeShells [manager_shell cli_shell kardinal_shell cli_kontrol_api_shell] diff --git a/sidecars/redis-overlay-service/default.nix b/sidecars/redis-overlay-service/default.nix new file mode 100644 index 00000000..b0e32cfb --- /dev/null +++ b/sidecars/redis-overlay-service/default.nix @@ -0,0 +1,20 @@ +{ + pkgs, + commit_hash ? "dirty", +}: let + pname = "redis-proxy-overlay"; + ldflags = pkgs.lib.concatStringsSep "\n" [ + "-X github.com/kurtosis-tech/kurtosis/kardinal.AppName=${pname}" + "-X github.com/kurtosis-tech/kurtosis/kardinal.Commit=${commit_hash}" + ]; +in + pkgs.buildGoApplication { + # pname has to match the location (folder) where the main function is or use + # subPackges to specify the file (e.g. subPackages = ["some/folder/main.go"];) + inherit pname ldflags; + name = "${pname}"; + pwd = ./.; + src = ./.; + modules = ./gomod2nix.toml; + CGO_ENABLED = 0; + } diff --git a/sidecars/redis-overlay-service/go.mod b/sidecars/redis-overlay-service/go.mod new file mode 100644 index 00000000..c13a4cf5 --- /dev/null +++ b/sidecars/redis-overlay-service/go.mod @@ -0,0 +1,15 @@ +module kardinal.stateful.overlay.redis + +go 1.22.3 + +require ( + github.com/redis/go-redis/v9 v9.5.2 + github.com/tidwall/redcon v1.6.2 +) + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/tidwall/btree v1.7.0 // indirect + github.com/tidwall/match v1.1.1 // indirect +) diff --git a/sidecars/redis-overlay-service/go.sum b/sidecars/redis-overlay-service/go.sum new file mode 100644 index 00000000..ca361903 --- /dev/null +++ b/sidecars/redis-overlay-service/go.sum @@ -0,0 +1,17 @@ +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/redis/go-redis/v9 v9.5.2 h1:L0L3fcSNReTRGyZ6AqAEN0K56wYeYAwapBIhkvh0f3E= +github.com/redis/go-redis/v9 v9.5.2/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4= +github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= +github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow= +github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y= diff --git a/sidecars/redis-overlay-service/gomod2nix.toml b/sidecars/redis-overlay-service/gomod2nix.toml new file mode 100644 index 00000000..f2cb1cd0 --- /dev/null +++ b/sidecars/redis-overlay-service/gomod2nix.toml @@ -0,0 +1,417 @@ +schema = 3 + +[mod] + [mod."cloud.google.com/go"] + version = "v0.54.0" + hash = "sha256-0GHaPyaZ6PtSrRPoVDs1zWxgo9kwSm/cxHCD0+Q6C8g=" + [mod."cloud.google.com/go/bigquery"] + version = "v1.4.0" + hash = "sha256-niyEg/99kFPoAnQ5CQHfS8GEfQYGKtNbD36nNuSF4zo=" + [mod."cloud.google.com/go/datastore"] + version = "v1.1.0" + hash = "sha256-wyOuic/vp33s1QiybElwERALQwPrn8gFHDAdlTBcv1Q=" + [mod."cloud.google.com/go/pubsub"] + version = "v1.2.0" + hash = "sha256-pVKDdQS2A1P0inU6mkI1aUrfIuHgL5ib8RpKt/rYQVI=" + [mod."cloud.google.com/go/storage"] + version = "v1.6.0" + hash = "sha256-pF2Ff0UVveFDI5yDX1G3MV4bCx++x+mTQ4OIHgPIGqA=" + [mod."dmitri.shuralyov.com/gpu/mtl"] + version = "v0.0.0-20190408044501-666a987793e9" + hash = "sha256-H+xcbVdCNDahWZPgwk4k+XxnM73g0hwaFY7x+OAATcc=" + [mod."github.com/Azure/go-autorest"] + version = "v14.2.0+incompatible" + hash = "sha256-dvWOcudtx0NP6U2RDt40hwtELFRdYdLEklRWYterRN0=" + [mod."github.com/Azure/go-autorest/autorest"] + version = "v0.11.18" + hash = "sha256-j6IViAvhZ5r06n1qpGVy4aUF/pEYhQknkTQJa1qFtBI=" + [mod."github.com/Azure/go-autorest/autorest/adal"] + version = "v0.9.13" + hash = "sha256-hPrY0ZmA/x6NDCViEdlg44nPjQirxA4ct5SzUMp43NY=" + [mod."github.com/Azure/go-autorest/autorest/date"] + version = "v0.3.0" + hash = "sha256-PWFHUZ9jMJ6gkMCnRpR89s/aI3YdtzskIePj8Ulu4dc=" + [mod."github.com/Azure/go-autorest/autorest/mocks"] + version = "v0.4.1" + hash = "sha256-ZzK6AL03DbclhBC8gF9bnQadnAn5hQSEXOlmglZMCEg=" + [mod."github.com/Azure/go-autorest/logger"] + version = "v0.2.1" + hash = "sha256-xGqpcF7fL1MQCN4xXTHpAFDzEA5f4p6kdb9yV1+uB4k=" + [mod."github.com/Azure/go-autorest/tracing"] + version = "v0.6.0" + hash = "sha256-CcLYoOyRcMo4aRRYN+TBbaHtJqDra4e0qo3cmGZIB74=" + [mod."github.com/BurntSushi/toml"] + version = "v0.3.1" + hash = "sha256-Rqak1dE/Aj/+Kx1/pl3Hifgt+Q3OzuZ5fJR+/x3nTbo=" + [mod."github.com/BurntSushi/xgb"] + version = "v0.0.0-20160522181843-27f122750802" + hash = "sha256-ck+gNOSXNYy/ji6mpSX3OTHgCDm2nww+3ZKu4lAXl6I=" + [mod."github.com/NYTimes/gziphandler"] + version = "v0.0.0-20170623195520-56545f4a5d46" + hash = "sha256-4mTVrxEH1Cu3MVhm/nB+Zm8b2oYS4SecOHjnbT5Pk7s=" + [mod."github.com/PuerkitoBio/purell"] + version = "v1.1.1" + hash = "sha256-Hjf8ZNNdwcRd50A9QNrcCj17gu/6f1iKzVTVrrMrojA=" + [mod."github.com/PuerkitoBio/urlesc"] + version = "v0.0.0-20170810143723-de5bf2ad4578" + hash = "sha256-nL0/0QM0Pec83vBlwXvQ8g5SvvZnCQgzD1apxfHNGlg=" + [mod."github.com/asaskevich/govalidator"] + version = "v0.0.0-20190424111038-f61b66f89f4a" + hash = "sha256-pcrINvdGpQExaSQv1j19L0NLWcvZL8jXQMsvMlVq8ss=" + [mod."github.com/bsm/ginkgo/v2"] + version = "v2.12.0" + hash = "sha256-rZ9AF2RPWwQcgFmh3bmUXDaTLL14fQfPtv+DuC8xqzQ=" + [mod."github.com/bsm/gomega"] + version = "v1.27.10" + hash = "sha256-EltjBddI8lJOxiONk2GgbjtHp7ysV00CK7BRVbAOLZ4=" + [mod."github.com/census-instrumentation/opencensus-proto"] + version = "v0.2.1" + hash = "sha256-3RWTfGGuKbkrOQ91ociOgp9igGvU/FAv3NAidPVoYP8=" + [mod."github.com/cespare/xxhash/v2"] + version = "v2.3.0" + hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY=" + [mod."github.com/chzyer/logex"] + version = "v1.1.10" + hash = "sha256-BNOaV/CFAqOymWW3R2m1sCikdCwFZM/pVkylzoeU6yI=" + [mod."github.com/chzyer/readline"] + version = "v0.0.0-20180603132655-2972be24d48e" + hash = "sha256-2Uj5LGpHEbLQG3d/7z9AL8DknUBZyoTAMs4j+VVDmIA=" + [mod."github.com/chzyer/test"] + version = "v0.0.0-20180213035817-a1ea475d72b1" + hash = "sha256-U0irpUSqegh7Nzg1ErPuyjESOcIXXOWf7ikKMbES2mY=" + [mod."github.com/client9/misspell"] + version = "v0.3.4" + hash = "sha256-MIKnt4va/nPl+5cCgOvCyRGIORTnguieQhlj8ery4BU=" + [mod."github.com/creack/pty"] + version = "v1.1.9" + hash = "sha256-Rj6c4/3IApJcS36iPVIEdlMSC/SWmywnpqk1500ik5k=" + [mod."github.com/davecgh/go-spew"] + version = "v1.1.1" + hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI=" + [mod."github.com/dgryski/go-rendezvous"] + version = "v0.0.0-20200823014737-9f7001d12a5f" + hash = "sha256-n/7xo5CQqo4yLaWMSzSN1Muk/oqK6O5dgDOFWapeDUI=" + [mod."github.com/docopt/docopt-go"] + version = "v0.0.0-20180111231733-ee0de3bc6815" + hash = "sha256-0mCKIC5x7aauBL8ahXB9ExMfoTJl55HaafWWWPNRmUI=" + [mod."github.com/elazarl/goproxy"] + version = "v0.0.0-20180725130230-947c36da3153" + hash = "sha256-aZb2tgdNc9f8k1wdT8+y71+AFJzgH4skELWhHwRVGaQ=" + [mod."github.com/emicklei/go-restful"] + version = "v0.0.0-20170410110728-ff4f55a20633" + hash = "sha256-LAhmOVQtqh1TKYhyzanQl/R24evmPr/2G38pQUKRtOw=" + [mod."github.com/envoyproxy/go-control-plane"] + version = "v0.9.1-0.20191026205805-5f8ba28d4473" + hash = "sha256-DFjKGsJUiBsD0+6E5zgyUDhjM2MWWqb3a7/0UueFuvg=" + [mod."github.com/envoyproxy/protoc-gen-validate"] + version = "v0.1.0" + hash = "sha256-2htufg3hdOHfbDVI5wFpsuaiDIhH6O1taUGJMdVbjlQ=" + [mod."github.com/evanphx/json-patch"] + version = "v4.11.0+incompatible" + hash = "sha256-GiV4D+OaD/onbgi51lmklA+ULGKOjm+R1Ej4Jnxp8Bc=" + [mod."github.com/form3tech-oss/jwt-go"] + version = "v3.2.3+incompatible" + hash = "sha256-2YU7EMWUrpWepYr0WXbiZjt7t4dhNeNq+e/IRQdNAG0=" + [mod."github.com/fsnotify/fsnotify"] + version = "v1.4.9" + hash = "sha256-HZBMhbFqcZM9oxSbWqISzEE7GF7JZpco5tyta6c4OcQ=" + [mod."github.com/go-gl/glfw"] + version = "v0.0.0-20190409004039-e6da0acd62b1" + hash = "sha256-tqPStzM1xOuEWqAv4pBbzB+lNIxEqqyCCP0wWCbrlyY=" + [mod."github.com/go-gl/glfw/v3.3/glfw"] + version = "v0.0.0-20200222043503-6f7a984d4dc4" + hash = "sha256-6BfEsip1tEBelFTsKVtn2okCTb+0UsqEdIljg+PIjiE=" + [mod."github.com/go-logr/logr"] + version = "v0.4.0" + hash = "sha256-xeGTv0SMhCkLbIOH5Su2q/74B+OeOxsyEVYtQepF3/E=" + [mod."github.com/go-openapi/jsonpointer"] + version = "v0.19.3" + hash = "sha256-WZL/QvFB0OYyvHPNRhkl1BI0NT0TFMqRFlojX9hZi9Q=" + [mod."github.com/go-openapi/jsonreference"] + version = "v0.19.3" + hash = "sha256-XDP7dJdwIl/AYU85bpKXX3twGJBlMhIrt96RE1UPwtI=" + [mod."github.com/go-openapi/swag"] + version = "v0.19.5" + hash = "sha256-D8qq6ZbWhLNroEisriI5d4zea5yQ7pzV/tl8XxjeZPw=" + [mod."github.com/gogo/protobuf"] + version = "v1.3.2" + hash = "sha256-pogILFrrk+cAtb0ulqn9+gRZJ7sGnnLLdtqITvxvG6c=" + [mod."github.com/golang/glog"] + version = "v0.0.0-20160126235308-23def4e6c14b" + hash = "sha256-YDyL9TRikSXHSrYtITVA/ovYIYrdnZGym14XnslAYkk=" + [mod."github.com/golang/groupcache"] + version = "v0.0.0-20210331224755-41bb18bfe9da" + hash = "sha256-7Gs7CS9gEYZkbu5P4hqPGBpeGZWC64VDwraSKFF+VR0=" + [mod."github.com/golang/mock"] + version = "v1.4.1" + hash = "sha256-5LaaCRbIvFrATowswmQRe+JRRcb4v7u67Qxf21YSw/E=" + [mod."github.com/golang/protobuf"] + version = "v1.5.2" + hash = "sha256-IVwooaIo46iq7euSSVWTBAdKd+2DUaJ67MtBao1DpBI=" + [mod."github.com/google/btree"] + version = "v1.0.1" + hash = "sha256-1PIeFGgUL4BK/StL/D12pg9bEQ5HfMT/fMLdus4pZTs=" + [mod."github.com/google/go-cmp"] + version = "v0.5.5" + hash = "sha256-pyfTIu1fx6cGYHhm+yi8YP2TgERnnqKRjqSV7WGb1Yk=" + [mod."github.com/google/gofuzz"] + version = "v1.1.0" + hash = "sha256-PSNufXo20YXfSNbayCc3hdujKZT5EW4lybsQ+WJ0Imo=" + [mod."github.com/google/martian"] + version = "v2.1.0+incompatible" + hash = "sha256-N3tPu89U5MQqmtFIqSEfqEXNgnHf883TAmXKvA2N8KQ=" + [mod."github.com/google/pprof"] + version = "v0.0.0-20200229191704-1ebb73c60ed3" + hash = "sha256-bogfN5hWzqf2kxEyA2XsUQu41nPcOBSvs1Z77x2zseI=" + [mod."github.com/google/renameio"] + version = "v0.1.0" + hash = "sha256-XQ5yI+LMfFQuK7+T3Xx5jiaRP7GmiQSsPkFmm1TpIs4=" + [mod."github.com/google/uuid"] + version = "v1.1.2" + hash = "sha256-DXttjObhEiMn5/OH+mYkJU6u03Gwsx5t08lTsIFyd+U=" + [mod."github.com/googleapis/gax-go/v2"] + version = "v2.0.5" + hash = "sha256-2ibpBbDxLVeYHd8gdszHb3w8rgKrChbUNlkaxW9lIhU=" + [mod."github.com/googleapis/gnostic"] + version = "v0.5.5" + hash = "sha256-KpSB2tHJsikuI86gZFCOod1MAbZEiiSrLCmVoAcm9X8=" + [mod."github.com/gorilla/websocket"] + version = "v1.4.2" + hash = "sha256-GhBLM/XTm2lFCyDvJbnCLAI2OyYXQV6W+jRPOQ1PdVY=" + [mod."github.com/gregjones/httpcache"] + version = "v0.0.0-20180305231024-9cad4c3443a7" + hash = "sha256-2ngFfFuSm8YSTNZWGQuN5yTpsXlwY2R8aaIzjDnjTXI=" + [mod."github.com/hashicorp/golang-lru"] + version = "v0.5.1" + hash = "sha256-/tr/EXgTmXPqrrx6kdMPMfWSC6GBdCPlX8GEuRk4yI0=" + [mod."github.com/hpcloud/tail"] + version = "v1.0.0" + hash = "sha256-7ByBr/RcOwIsGPCiCUpfNwUSvU18QAY+HMnCJr8uU1w=" + [mod."github.com/ianlancetaylor/demangle"] + version = "v0.0.0-20181102032728-5e5cf60278f6" + hash = "sha256-5cS82VAt2PtDlx18XzvNwhp7zk/fryC9HCzdyEKYEro=" + [mod."github.com/imdario/mergo"] + version = "v0.3.5" + hash = "sha256-644gtN58Py6dYU8OAM10R95YuD2W4Aj3ZS+NuxOyb9c=" + [mod."github.com/json-iterator/go"] + version = "v1.1.11" + hash = "sha256-/rcZZy458AsEqrSgtneGghVUpHNXJfT66QQHI6hyERU=" + [mod."github.com/jstemmer/go-junit-report"] + version = "v0.9.1" + hash = "sha256-fK6zLXQU8y+h+SqyeCUldA5OFPA/j0Wlbizk6AG60c4=" + [mod."github.com/kisielk/errcheck"] + version = "v1.5.0" + hash = "sha256-ZmocFXtg+Thdup+RqDYC/Td3+m1nS0FydZecfsWXIzI=" + [mod."github.com/kisielk/gotool"] + version = "v1.0.0" + hash = "sha256-lsdQkue8gFz754PGqczUnvGiCQq87SruQtdrDdQVTpE=" + [mod."github.com/kr/pretty"] + version = "v0.2.0" + hash = "sha256-127gkrb5HVPOCp0sGZOmiJPaQuQ4zptRw0MNGP53i/s=" + [mod."github.com/kr/pty"] + version = "v1.1.1" + hash = "sha256-AVeS+ivwNzIrgWQaLtsmO2f2MYGpxIVqdac/EzaYI1Q=" + [mod."github.com/kr/text"] + version = "v0.2.0" + hash = "sha256-fadcWxZOORv44oak3jTxm6YcITcFxdGt4bpn869HxUE=" + [mod."github.com/mailru/easyjson"] + version = "v0.0.0-20190626092158-b2ccc519800e" + hash = "sha256-6tSxxb/n5O4/UYZRXHYYfi0WZBWX1gIiQclBC0oQlJI=" + [mod."github.com/mitchellh/mapstructure"] + version = "v1.1.2" + hash = "sha256-OU9HZYHtl0qaqMFd84w7snkkRuRY6UMSsfCnL5HYdw0=" + [mod."github.com/moby/spdystream"] + version = "v0.2.0" + hash = "sha256-Feme5UoWuBCvnLPKw1ozKkF3SM7PAjjPFQZ3TJhghR0=" + [mod."github.com/modern-go/concurrent"] + version = "v0.0.0-20180306012644-bacd9c7ef1dd" + hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo=" + [mod."github.com/modern-go/reflect2"] + version = "v1.0.1" + hash = "sha256-5D1HGVBc/REwPVdlPYcXsbZM80OIh7V5uiyKAbMA5qo=" + [mod."github.com/munnerz/goautoneg"] + version = "v0.0.0-20120707110453-a547fc61f48d" + hash = "sha256-ESdY/qfmo5a9Mwxccq3MMBde4DsoTdPO9WFj2IuFcKY=" + [mod."github.com/mxk/go-flowrate"] + version = "v0.0.0-20140419014527-cca7078d478f" + hash = "sha256-gRTfRfff/LRxC1SXXnQd2tV3UTcTx9qu90DJIVIaGn8=" + [mod."github.com/niemeyer/pretty"] + version = "v0.0.0-20200227124842-a10e7caefd8e" + hash = "sha256-m2D7hWZrDst0rb91lmjSuNrzBQbmQ0Oe2UOp3wn8qso=" + [mod."github.com/nxadm/tail"] + version = "v1.4.4" + hash = "sha256-S336Q+5j2uVmwLOfEUpU0iFBEEDH+UtdQO0NWPsc9OY=" + [mod."github.com/onsi/ginkgo"] + version = "v1.14.0" + hash = "sha256-Vv2tuMhqoNc76LJkV79SUQWTtFoEXnHj11hW4gSTr0Y=" + [mod."github.com/onsi/gomega"] + version = "v1.10.1" + hash = "sha256-uIn5IR1oT3Q9eXbLsBlIbXJRYEIk401xSzZRvlqOFRk=" + [mod."github.com/peterbourgon/diskv"] + version = "v2.0.1+incompatible" + hash = "sha256-K4mEVjH0eyxyYHQRxdbmgJT0AJrfucUwGB2BplRRt9c=" + [mod."github.com/pkg/errors"] + version = "v0.9.1" + hash = "sha256-mNfQtcrQmu3sNg/7IwiieKWOgFQOVVe2yXgKBpe/wZw=" + [mod."github.com/pmezard/go-difflib"] + version = "v1.0.0" + hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA=" + [mod."github.com/prometheus/client_model"] + version = "v0.0.0-20190812154241-14fe0d1b01d4" + hash = "sha256-oJSU4o77UVps0e2SJUDz6enypNipZPDdZmn0tbKZtX0=" + [mod."github.com/redis/go-redis/v9"] + version = "v9.5.2" + hash = "sha256-5LAHJam4xU7MvQxL91UL66Zx4UTLCE6hrtv5A+JCpw4=" + [mod."github.com/rogpeppe/go-internal"] + version = "v1.3.0" + hash = "sha256-JgiasZeXDy10syy7wmXtqRffDY7CJ1o5VNY+FmmAjVU=" + [mod."github.com/spf13/afero"] + version = "v1.2.2" + hash = "sha256-SZVSWEvRzFGZm5u384Rp/LU0dyJ00oIqEQOV9nAxOUk=" + [mod."github.com/spf13/pflag"] + version = "v1.0.5" + hash = "sha256-w9LLYzxxP74WHT4ouBspH/iQZXjuAh2WQCHsuvyEjAw=" + [mod."github.com/stoewer/go-strcase"] + version = "v1.2.0" + hash = "sha256-Vc8Ecx5DBeAK89TgqxaHV7TUrpjGMUdV6cOc3FM297A=" + [mod."github.com/stretchr/objx"] + version = "v0.1.0" + hash = "sha256-az0Vd4MG3JXfaYbj0Q6AOmNkrXgmXDeQm8+BBiDXmdA=" + [mod."github.com/stretchr/testify"] + version = "v1.7.0" + hash = "sha256-t1I9uCrn9vSUu/z5IZuNyGShmbOcJ6UGc2f75ZBrHzE=" + [mod."github.com/tidwall/btree"] + version = "v1.7.0" + hash = "sha256-bnr6c7a0nqo2HyGqxHk0kEZCEsjLYkPbAVY9WzaZ30o=" + [mod."github.com/tidwall/match"] + version = "v1.1.1" + hash = "sha256-M2klhPId3Q3T3VGkSbOkYl/2nLHnsG+yMbXkPkyrRdg=" + [mod."github.com/tidwall/redcon"] + version = "v1.6.2" + hash = "sha256-EhMOHoYAucg7ahRc9ALiEOluvJCAVd9nwy7BKfDt6bc=" + [mod."github.com/yuin/goldmark"] + version = "v1.2.1" + hash = "sha256-vrL87IsRgYTTHvCH2fodVu+ECk9UCu4kuCy3Ypy2Oos=" + [mod."go.opencensus.io"] + version = "v0.22.3" + hash = "sha256-A6qNyBe3ubB3hu8VJKEzo4uohvhP3dI6hOwItlvxVW8=" + [mod."golang.org/x/crypto"] + version = "v0.0.0-20210220033148-5ea612d1eb83" + hash = "sha256-ZacGMV1CFBAmShBQ/dN+C8Ls5ECHXXZdUiI1hcu4cFU=" + [mod."golang.org/x/exp"] + version = "v0.0.0-20200224162631-6cc2880d07d6" + hash = "sha256-bhrVk10F9h0g3uRn+rJEA66KqUNy6fG2H56T04KfXPk=" + [mod."golang.org/x/image"] + version = "v0.0.0-20190802002840-cff245a6509b" + hash = "sha256-BP2l1VUXd5afv4fsZ9g6WYy6zEPY782ZAsMrFSe1P0I=" + [mod."golang.org/x/lint"] + version = "v0.0.0-20200302205851-738671d3881b" + hash = "sha256-LNkJ9QCzsDgFjrLI+hYNISu9jrsu9tX9tFKm4i/5cUo=" + [mod."golang.org/x/mobile"] + version = "v0.0.0-20190719004257-d2bd2a29d028" + hash = "sha256-At0uE2mTr/GHCyF4U8Z+AiU2jlvBVQuX25tooo2ll6M=" + [mod."golang.org/x/mod"] + version = "v0.6.0-dev.0.20220419223038-86c51ed26bb4" + hash = "sha256-HS5GtzK6mLkQ/FM+GWoX4wSW0XllHK/2O3Y7O2/sFBk=" + [mod."golang.org/x/net"] + version = "v0.0.0-20210520170846-37e1c6afe023" + hash = "sha256-dwPXxrv+JephmrlF5fRdypRTd4ka/VFx7jbvJEzOXr0=" + [mod."golang.org/x/oauth2"] + version = "v0.0.0-20200107190931-bf48bf16ab8d" + hash = "sha256-r+VK6vgr8pMOmLXonG6YhBSUQ+kZJzdHgpV/YFZsOeo=" + [mod."golang.org/x/sync"] + version = "v0.0.0-20201020160332-67f06af15bc9" + hash = "sha256-hfvviA1/duBMtleic86KvYNlUz3/V7XKVAggZtUldyQ=" + [mod."golang.org/x/sys"] + version = "v0.0.0-20220722155257-8c9f86f7a55f" + hash = "sha256-mXgFTb2vfUEjTPA6PX7CZTvKGJWaxoU2QplyI1GjC+w=" + [mod."golang.org/x/term"] + version = "v0.0.0-20210220032956-6a3ed077a48d" + hash = "sha256-BQ9U/OJuxKwVGsfMff6reDsBTmBiDSGmMy5lgYdF0XY=" + [mod."golang.org/x/text"] + version = "v0.3.8" + hash = "sha256-KKHU0OMfT4oxTIzIVqan8MY8wNvgBW00s24hF+kWbMA=" + [mod."golang.org/x/time"] + version = "v0.0.0-20210723032227-1f47c861a9ac" + hash = "sha256-YygVh3tohBFn6Or1txGRgeaCaCoMJLhEmKaKxLkjLbc=" + [mod."golang.org/x/tools"] + version = "v0.1.12" + hash = "sha256-D0kGneGMt+LFbdUDo9Axd0yB0a5t3Z0YKVAnJDDaBo8=" + [mod."golang.org/x/xerrors"] + version = "v0.0.0-20200804184101-5ec99f83aff1" + hash = "sha256-62f++IO8Ia32CYy+xX8XDxCcT9r1sbPvVwoKV99gf7U=" + [mod."google.golang.org/api"] + version = "v0.20.0" + hash = "sha256-m7nqVlRss1EJbNEn7uIOXxXdspqNtlcxpMQotWCgJm8=" + [mod."google.golang.org/appengine"] + version = "v1.6.5" + hash = "sha256-cE9jZUiPSk7GMhhpdcl4NS6OkGqwh9IU3exKiqs+3ns=" + [mod."google.golang.org/genproto"] + version = "v0.0.0-20201019141844-1ed22bb0c154" + hash = "sha256-LjPOXSbb9owLJQmyoI8Zwys/zwu69kZuNUU6HxZW5v8=" + [mod."google.golang.org/grpc"] + version = "v1.27.1" + hash = "sha256-jPk1fgEH3hVbXuSCY2kGXdWZoRom+wkmnx82fyrxnB4=" + [mod."google.golang.org/protobuf"] + version = "v1.33.0" + hash = "sha256-cWwQjtUwSIEkAlAadrlxK1PYZXTRrV4NKzt7xDpJgIU=" + [mod."gopkg.in/check.v1"] + version = "v1.0.0-20200227125254-8fa46927fb4f" + hash = "sha256-nGV66SlDrpDfhkzlr9h52JybsCioW3ix5bKmbkEfoDs=" + [mod."gopkg.in/errgo.v2"] + version = "v2.1.0" + hash = "sha256-Ir/MuxQFxvVJEciovGOZbM8ZfKJ/AYotPwYfH2FctRg=" + [mod."gopkg.in/fsnotify.v1"] + version = "v1.4.7" + hash = "sha256-j/Ts92oXa3k1MFU7Yd8/AqafRTsFn7V2pDKCyDJLah8=" + [mod."gopkg.in/inf.v0"] + version = "v0.9.1" + hash = "sha256-z84XlyeWLcoYOvWLxPkPFgLkpjyb2Y4pdeGMyySOZQI=" + [mod."gopkg.in/tomb.v1"] + version = "v1.0.0-20141024135613-dd632973f1e7" + hash = "sha256-W/4wBAvuaBFHhowB67SZZfXCRDp5tzbYG4vo81TAFdM=" + [mod."gopkg.in/yaml.v2"] + version = "v2.4.0" + hash = "sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0=" + [mod."gopkg.in/yaml.v3"] + version = "v3.0.0" + hash = "sha256-XWfvIV7+KXlZV4sBZd0srGeuQonS9cTYptCc6NcgVvc=" + [mod."honnef.co/go/tools"] + version = "v0.0.1-2020.1.3" + hash = "sha256-UI1MNNdyH+nfW7ShzHHTNNlmxOLeJVMSgf8ZfQtcEV8=" + [mod."k8s.io/api"] + version = "v0.22.2" + hash = "sha256-DXAn/q5GE86vcO3KOHk30Ve9f6DDl5ooHWTG1kll3Yk=" + [mod."k8s.io/apimachinery"] + version = "v0.22.2" + hash = "sha256-TUg+uQWYw/k9GtBoFoparT9+GReG/zY1HvDgReAH/4w=" + [mod."k8s.io/client-go"] + version = "v0.22.2" + hash = "sha256-DAmH1NENDtLkxBsipHS5xEm8uagCkGrjoGr5tGGC8hk=" + [mod."k8s.io/gengo"] + version = "v0.0.0-20200413195148-3a45101e95ac" + hash = "sha256-QSlK7SjCMtlY2OuJCFYQXzZqvLotsMWF0D29yyciGyg=" + [mod."k8s.io/klog/v2"] + version = "v2.9.0" + hash = "sha256-k8Zj7Ypn6UJRyprmWD3bvgoAXQI1CmPIjqugsdRBYFk=" + [mod."k8s.io/kube-openapi"] + version = "v0.0.0-20210421082810-95288971da7e" + hash = "sha256-Jua9Coid7u8Zqzvzas7u4eVp5KIHwYGWw8Q7ZqEec9s=" + [mod."k8s.io/utils"] + version = "v0.0.0-20210819203725-bdf08cb9a70a" + hash = "sha256-twbsCnkBI6tf0oRp2K7mfBAkom0QFUmynwnoa5ggFfk=" + [mod."rsc.io/binaryregexp"] + version = "v0.2.0" + hash = "sha256-izALTmzybQe67BNXliqQ3xCEOo+b6o8C4YoX5H0FWc0=" + [mod."rsc.io/quote/v3"] + version = "v3.1.0" + hash = "sha256-sF7lOq/bInDPtLI+j610WJRAQ09Cxc5lmPzzytuE6DI=" + [mod."rsc.io/sampler"] + version = "v1.3.0" + hash = "sha256-UPbUO3GOGn6/fz1EBEYONBX45V6bzQKQv6CoZb2Y3S8=" + [mod."sigs.k8s.io/structured-merge-diff/v4"] + version = "v4.1.2" + hash = "sha256-3K0/Dl649UR/f067hi7kOoXdiTHJuWlMZKeNOvJFA2k=" + [mod."sigs.k8s.io/yaml"] + version = "v1.2.0" + hash = "sha256-HHqIfaFuKGalif3bP2CcHm8MT8Ily1kJQL9Q8Kz2WHA=" diff --git a/sidecars/redis-overlay-service/redis-proxy-overlay/main.go b/sidecars/redis-overlay-service/redis-proxy-overlay/main.go new file mode 100644 index 00000000..051b8954 --- /dev/null +++ b/sidecars/redis-overlay-service/redis-proxy-overlay/main.go @@ -0,0 +1,221 @@ +package main + +import ( + "context" + "log" + "os" + "strconv" + "strings" + "sync" + + "github.com/redis/go-redis/v9" + "github.com/tidwall/redcon" +) + +func main() { + var mu sync.RWMutex + items := make(map[string][]byte) + var ps redcon.PubSub + redisAddr := os.Getenv("REDIS_ADDR") + addr := ":" + os.Getenv("PORT") + + go log.Printf("started server at %s", addr) + + rdb := redis.NewClient(&redis.Options{ + Addr: redisAddr, + Password: "", // no password set + DB: 0, // use default DB + }) + + err := redcon.ListenAndServe(addr, + func(conn redcon.Conn, cmd redcon.Command) { + argsIndex := 0 + for argsIndex < len(cmd.Args) { + log.Printf("Command arg %d: '%s'", argsIndex, string(cmd.Args[argsIndex])) + argsIndex += 1 + } + switch strings.ToLower(string(cmd.Args[0])) { + default: + conn.WriteError("ERR unknown command '" + string(cmd.Args[0]) + "'") + case "publish": + // Publish to all pub/sub subscribers and return the number of + // messages that were sent. + if len(cmd.Args) != 3 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + count := ps.Publish(string(cmd.Args[1]), string(cmd.Args[2])) + conn.WriteInt(count) + case "subscribe", "psubscribe": + // Subscribe to a pub/sub channel. The `Psubscribe` and + // `Subscribe` operations will detach the connection from the + // event handler and manage all network I/O for this connection + // in the background. + if len(cmd.Args) < 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + command := strings.ToLower(string(cmd.Args[0])) + for i := 1; i < len(cmd.Args); i++ { + if command == "psubscribe" { + ps.Psubscribe(conn, string(cmd.Args[i])) + } else { + ps.Subscribe(conn, string(cmd.Args[i])) + } + } + case "detach": + hconn := conn.Detach() + log.Printf("connection has been detached") + go func() { + defer hconn.Close() + hconn.WriteString("OK") + hconn.Flush() + }() + case "ping": + conn.WriteString("PONG") + case "quit": + conn.WriteString("OK") + conn.Close() + case "set": + if len(cmd.Args) != 3 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + mu.Lock() + items[string(cmd.Args[1])] = cmd.Args[2] + mu.Unlock() + conn.WriteString("OK") + case "incrby": + if len(cmd.Args) != 3 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + incrby, _ := strconv.Atoi(string(cmd.Args[2])) + mu.RLock() + val, ok := items[string(cmd.Args[1])] + mu.RUnlock() + var valInt int + if ok { + var err error + valInt, err = strconv.Atoi(string(val[:])) + if err != nil { + conn.WriteError("Increment by a non integer") + return + } + // ##################################################################### + // TODO: Unless upsteam get! Only to genereate calls to show up in Kiali + rdb.Get(context.Background(), string(cmd.Args[1])).Result() + // ##################################################################### + } else { + // Get the upstream value if local write does not exist yet + upstreamVal, err := rdb.Get(context.Background(), string(cmd.Args[1])).Result() + if err == redis.Nil { + log.Printf("%v does not exist", string(cmd.Args[1])) + valInt = 0 + } else if err != nil { + log.Printf("Proxied get command failed: '%v'", err) + conn.WriteError("Proxied get command failed") + return + } else { + log.Printf("got upval: '%v'", upstreamVal) + valInt, err = strconv.Atoi(upstreamVal) + if err != nil { + log.Printf("Upstream value is not an Int: '%v'", err) + conn.WriteError("Upstream value for key '" + string(cmd.Args[1]) + "' is not an Int") + return + } + } + } + // Update the value in the local cache and return total value + mu.Lock() + items[string(cmd.Args[1])] = []byte(strconv.Itoa(valInt + incrby)) + mu.Unlock() + conn.WriteInt(valInt + incrby) + case "get": + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + mu.RLock() + val, ok := items[string(cmd.Args[1])] + mu.RUnlock() + if !ok { + res, err := rdb.Get(context.Background(), string(cmd.Args[1])).Result() + if err == redis.Nil { + conn.WriteNull() + return + } + if err != nil { + log.Printf("Proxied get command failed: '%v'", err) + conn.WriteError("Proxied get command failed") + return + } + mu.Lock() + items[string(cmd.Args[1])] = []byte(res) + mu.Unlock() + conn.WriteString(res) + } else { + conn.WriteBulk(val) + } + case "exists": + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + mu.Lock() + _, ok := items[string(cmd.Args[1])] + mu.Unlock() + if !ok { + _, err := rdb.Get(context.Background(), string(cmd.Args[1])).Result() + if err == redis.Nil { + conn.WriteInt(0) + return + } else if err != nil { + log.Printf("Proxied get command failed: '%v'", err) + conn.WriteError("Proxied get command failed") + return + } else { + conn.WriteInt(1) + return + } + } else { + conn.WriteInt(1) + } + case "del": + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + mu.Lock() + _, ok := items[string(cmd.Args[1])] + delete(items, string(cmd.Args[1])) + mu.Unlock() + if !ok { + conn.WriteInt(0) + } else { + conn.WriteInt(1) + } + case "echo": + if len(cmd.Args) != 2 { + conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") + return + } + conn.WriteBulk(cmd.Args[1]) + case "client": + conn.WriteString("OK") + } + }, + func(conn redcon.Conn) bool { + // Use this function to accept or deny the connection. + // log.Printf("accept: %s", conn.RemoteAddr()) + return true + }, + func(conn redcon.Conn, err error) { + // This is called when the connection has been closed + // log.Printf("closed: %s, err: %v", conn.RemoteAddr(), err) + }, + ) + if err != nil { + log.Fatal(err) + } +} diff --git a/sidecars/redis-overlay-service/shell.nix b/sidecars/redis-overlay-service/shell.nix new file mode 100644 index 00000000..894c97d9 --- /dev/null +++ b/sidecars/redis-overlay-service/shell.nix @@ -0,0 +1,17 @@ +{pkgs}: let + goEnv = pkgs.mkGoEnv {pwd = ./.;}; +in + pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + goEnv + + goreleaser + go + gopls + golangci-lint + delve + enumer + gomod2nix + bash-completion + ]; + }