diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100755 index 00000000..16492673 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,72 @@ +name: Build + +on: + push: {} + +jobs: + build: + name: 📦 Build & Test + runs-on: ubuntu-latest + outputs: + image: ${{ steps.meta.outputs.tags }} + steps: + - uses: actions/checkout@v3 + - uses: docker/setup-buildx-action@v2 + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch,pattern=snapshot-{{sha}} + labels: | + org.opencontainers.image.source=git@github.com:${{ github.repository }}.git + org.opencontainers.image.version=${{ github.head_ref || github.ref_name }} + org.opencontainers.image.revision=${{ github.sha }} + de.interhyp.image.servicename=metadata-service + - id: build-push + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + + release: + name: 🚀 Release + if: github.ref == 'refs/heads/main' + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - id: semantic-release + uses: go-semantic-release/action@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + prerelease: false + allow-initial-development-versions: true # remove to trigger an initial 1.0.0 release + changelog-generator-opt: "emojis=true" + hooks: goreleaser + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - id: repository-id + uses: ASzc/change-string-case-action@v6 + with: + string: ${{ github.repository }} + - if: steps.semantic-release.outputs.version != '' + run: | + TARGET=ghcr.io/${{ steps.repository-id.outputs.lowercase }}:${{ steps.semantic-release.outputs.version }} + SOURCE=${{ needs.build.outputs.image }} + + docker pull $SOURCE + docker tag $SOURCE $TARGET + docker push $TARGET diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..55b4edf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,49 @@ +# 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 +*jar + +# 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 + +# IntelliJ project files +*.iml +*.iws +*.ipr +.idea/ + +# Eclipse project file +.settings/ +.classpath +.project + +# NetBeans specific +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +nbactions.xml +nb-configuration.xml + +# OS +.DS_Store + +# Project specific +local-config.yaml +node_modules/ diff --git a/.releaserc.yaml b/.releaserc.yaml new file mode 100644 index 00000000..a5152df2 --- /dev/null +++ b/.releaserc.yaml @@ -0,0 +1,6 @@ +preset: angular +plugins: + - "@semantic-release/commit-analyzer" + - "@semantic-release/release-notes-generator" + - "@semantic-release/changelog" + - "@semantic-release/git" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..fc3de428 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,22 @@ +ARG GOLANG_VERSION=1 + +FROM golang:${GOLANG_VERSION} AS build + +COPY . /app +WORKDIR /app + +ARG CGO_ENABLED=0 +ARG GOOS=linux +ARG GOARCH=amd64 + +RUN go build main.go \ + && go test -v ./... \ + && go vet ./... + +FROM scratch + +COPY --from=build /app/main /main +COPY --from=build /etc/ssl/certs /etc/ssl/certs +COPY --from=build /app/api/openapi.yaml /api/openapi.yaml + +ENTRYPOINT ["/main"] diff --git a/LICENSE b/LICENSE index 43eb7b7f..a482921f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Elias R. +Copyright (c) 2025 Elias R. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 965b8e84..4808ffa8 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ -# under-construction \ No newline at end of file +# ToDos + +1. Improve API spec +2. Implement missing endpoints +3. Tests +4. Improve error handling +5. Support OCI +6. Support configurable filesystems (memory + disk) diff --git a/api/openapi.yaml b/api/openapi.yaml new file mode 100644 index 00000000..5cdc89bc --- /dev/null +++ b/api/openapi.yaml @@ -0,0 +1,466 @@ +openapi: 3.1.0 +info: + contact: + email: e.rieb@posteo.de + name: Elias R. + url: https://github.com/Roshick + description: Renders Kubernetes manifests with the help of various tools such as + Helm and Kustomize. + license: + name: Copyright (c) 2025 Elias R. + url: https://github.com/Roshick + title: Manifest Maestro + version: v1 +tags: + - name: Helm + - name: Kustomize + - name: Management +paths: + /rest/api/v1/helm/actions/list-charts: + post: + summary: List all Helm Charts + description: |- + Lists all Helm Charts contained in the given repository. + + Service needs read permission on given repository. + tags: + - Helm + operationId: postHelmListChartAction + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/HelmListChartsAction' + required: true + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/HelmListChartsActionResponse' + description: Success + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + "502": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Upstream failed to respond + /rest/api/v1/helm/actions/get-chart-metadata: + post: + summary: Get Helm Chart Metadata + description: |- + Retrieves metadata of chart contained in the given repository. + + Service needs read permission on given repository. + tags: + - Helm + operationId: postHelmGetChartMetadataAction + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/HelmGetChartMetadataAction' + required: true + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/HelmGetChartMetadataActionResponse' + description: Success + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "422": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Repository is in an unprocessable state + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + "502": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Upstream failed to respond + /rest/api/v1/helm/actions/render-chart: + post: + summary: Render Helm Chart + description: |- + Renders Kubernetes manifests based on Helm chart provided in a repository. + + Service needs read permission on given repository. + tags: + - Helm + operationId: postHelmRenderAction + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/HelmRenderChartAction' + required: true + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/HelmRenderChartActionResponse' + description: Success + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "422": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Repository is in an unprocessable state + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + "502": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Upstream failed to respond + /rest/api/v1/kustomize/actions/render-kustomization: + post: + summary: Render Kustomize Kustomization + description: |- + Renders Kubernetes manifests based on Kustomize Kustomization provided in repository. + + Service needs read permission on given repository. + tags: + - Kustomize + operationId: postKustomizeRenderKustomizationAction + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/KustomizeRenderKustomizationAction' + required: true + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/KustomizeRenderKustomizationActionResponse' + description: Success + "400": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Bad request + "422": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Repository is in an unprocessable state + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + "502": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Upstream failed to respond + /health/readiness: + get: + tags: + - Management + operationId: getReadinessHealth + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/HealthResponse' + description: OK + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error + /health/liveness: + get: + tags: + - Management + operationId: getLivenessHealth + responses: + "200": + content: + '*/*': + schema: + $ref: '#/components/schemas/HealthResponse' + description: OK + "500": + content: + '*/*': + schema: + $ref: '#/components/schemas/ErrorResponse' + description: Internal server error +components: + schemas: + HealthResponse: + type: object + properties: + description: + type: string + status: + type: string + ErrorResponse: + type: object + properties: + type: + type: string + title: + type: string + status: + type: number + detail: + type: string + instance: + type: string + HelmListChartsAction: + type: object + properties: + reference: + $ref: '#/components/schemas/HelmRepositoryReference' + required: + - reference + HelmListChartsActionResponse: + type: object + properties: + items: + type: array + items: + type: object + required: + - items + HelmGetChartMetadataAction: + type: object + properties: + reference: + $ref: '#/components/schemas/HelmChartReference' + required: + - reference + HelmGetChartMetadataActionResponse: + type: object + properties: + defaultValues: + type: object + required: + - defaultValues + HelmRenderChartAction: + type: object + properties: + reference: + $ref: '#/components/schemas/HelmChartReference' + parameters: + $ref: '#/components/schemas/HelmRenderParameters' + required: + - reference + HelmRenderChartActionResponse: + type: object + properties: + manifests: + items: + $ref: '#/components/schemas/Manifest' + type: array + metadata: + $ref: '#/components/schemas/HelmRenderMetadata' + KustomizeRenderKustomizationAction: + type: object + properties: + reference: + $ref: '#/components/schemas/KustomizationReference' + parameters: + $ref: '#/components/schemas/KustomizeRenderParameters' + required: + - reference + KustomizeRenderKustomizationActionResponse: + type: object + properties: + manifests: + type: array + items: + $ref: '#/components/schemas/Manifest' + HelmRepositoryReference: + oneOf: + - $ref: '#/components/schemas/HelmChartRepositoryReference' + - $ref: '#/components/schemas/GitRepositoryReference' + HelmChartRepositoryReference: + type: object + properties: + repositoryType: + type: string + repositoryURL: + type: string + required: + - repositoryType + - repositoryURL + GitRepositoryReference: + type: object + properties: + repositoryType: + type: string + repositoryURL: + type: string + gitReference: + type: string + required: + - repositoryType + - repositoryURL + - gitReference + HelmChartReference: + oneOf: + - $ref: '#/components/schemas/HelmChartRepositoryChartReference' + - $ref: '#/components/schemas/GitRepositoryPathReference' + HelmChartRepositoryChartReference: + allOf: + - $ref: '#/components/schemas/HelmChartRepositoryReference' + - properties: + chartName: + type: string + chartVersion: + type: string + required: + - chartName + KustomizationReference: + oneOf: + - $ref: '#/components/schemas/GitRepositoryPathReference' + GitRepositoryPathReference: + allOf: + - $ref: '#/components/schemas/GitRepositoryReference' + - properties: + path: + type: string + Manifest: + type: object + properties: + source: + type: string + content: + type: object + required: + - content + HelmRenderMetadata: + type: object + properties: + releaseName: + type: string + namespace: + type: string + apiVersions: + items: + type: string + type: array + kubeVersion: + type: string + helmVersion: + type: string + mergedValues: + type: object + required: + - releaseName + - namespace + - apiVersions + - kubeVersion + - helmVersion + - mergedValues + HelmRenderParameters: + type: object + properties: + releaseName: + type: string + namespace: + type: string + kubeVersion: + type: string + apiVersions: + items: + type: string + type: array + valueFiles: + items: + type: string + type: array + values: + additionalProperties: + type: string + valuesFlat: + items: + type: string + type: array + stringValues: + additionalProperties: + type: string + stringValuesFlat: + items: + type: string + type: array + complexValues: + type: object + includeCRDs: + default: true + type: boolean + includeHooks: + default: true + type: boolean + ignoreMissingValueFiles: + default: false + type: boolean + KustomizeRenderParameters: + type: object + properties: + manifestInjections: + items: + $ref: '#/components/schemas/KustomizeManifestInjection' + type: array + KustomizeManifestInjection: + type: object + properties: + fileName: + type: string + manifests: + items: + $ref: '#/components/schemas/Manifest' + type: array + required: + - fileName + - manifests diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..12f4891a --- /dev/null +++ b/go.mod @@ -0,0 +1,152 @@ +module github.com/Roshick/manifest-maestro + +go 1.23 + +require ( + github.com/Roshick/go-autumn-configloader v0.2.0 + github.com/Roshick/go-autumn-slog v0.3.1 + github.com/Roshick/go-autumn-synchronisation v0.7.5 + github.com/Roshick/go-autumn-vault v0.1.1 + github.com/StephanHCB/go-autumn-config-api v0.2.2 + github.com/StephanHCB/go-autumn-logging v0.4.0 + github.com/StephanHCB/go-autumn-web-swagger-ui v0.3.3 + github.com/go-chi/chi/v5 v5.2.0 + github.com/go-git/go-billy/v5 v5.6.1 + github.com/go-git/go-git/v5 v5.13.0 + github.com/google/uuid v1.6.0 + github.com/mitchellh/copystructure v1.2.0 + github.com/prometheus/client_golang v1.20.5 + gopkg.in/yaml.v3 v3.0.1 + helm.sh/helm/v3 v3.16.4 + sigs.k8s.io/kustomize/api v0.18.0 + sigs.k8s.io/yaml v1.4.0 +) + +require ( + dario.cat/mergo v1.0.1 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/ProtonMail/go-crypto v1.1.3 // indirect + github.com/StephanHCB/go-autumn-restclient v0.9.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudflare/circl v1.5.0 // indirect + github.com/containerd/containerd v1.7.23 // indirect + github.com/containerd/errdefs v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cyphar/filepath-securejoin v0.3.6 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/cli v25.0.1+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect + github.com/docker/docker v25.0.6+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-connections v0.5.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/emirpasic/gods v1.18.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/gobwas/glob v0.2.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/btree v1.0.1 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/huandu/xstrings v1.5.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kevinburke/ssh_config v1.2.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/mmcloughlin/avo v0.6.0 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pjbgf/sha1cd v0.3.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/redis/rueidis v1.0.52 // indirect + github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect + github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/skeema/knownhosts v1.3.0 // indirect + github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/x448/float16 v0.8.4 // indirect + github.com/xanzy/ssh-agent v0.3.3 // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xlab/treeprint v1.2.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect + golang.org/x/crypto v0.31.0 // indirect + golang.org/x/mod v0.22.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/time v0.3.0 // indirect + golang.org/x/tools v0.28.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/validator.v2 v2.0.1 // indirect + gopkg.in/warnings.v0 v0.1.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + k8s.io/api v0.31.3 // indirect + k8s.io/apiextensions-apiserver v0.31.3 // indirect + k8s.io/apimachinery v0.31.3 // indirect + k8s.io/cli-runtime v0.31.3 // indirect + k8s.io/client-go v0.31.3 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + oras.land/oras-go v1.2.5 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..230a1db3 --- /dev/null +++ b/go.sum @@ -0,0 +1,494 @@ +dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= +dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= +github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= +github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= +github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk= +github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/Roshick/go-autumn-configloader v0.2.0 h1:6nu8UKn3B3rqgq+YXIrfzfSswFdKiMChgraowNsub/Q= +github.com/Roshick/go-autumn-configloader v0.2.0/go.mod h1:s67wIWwgPV7BD94e2oFVZMPM6FDvXDPDugxDODbWKfk= +github.com/Roshick/go-autumn-slog v0.3.1 h1:WbcxXCyBbMK07W/eKSp6eDBRtA9eWQNeLopcFIoBkl0= +github.com/Roshick/go-autumn-slog v0.3.1/go.mod h1:3ssQHi5HlgYhfc5k4Z9CAabnfkR8oQ2PUw4yvENTnZo= +github.com/Roshick/go-autumn-synchronisation v0.7.5 h1:/HqG3S8Cx+2yPkIYKZIe2DOng+eYGF/1kWCSNpSOarY= +github.com/Roshick/go-autumn-synchronisation v0.7.5/go.mod h1:Zytfkb16g87fEv3x4g4LwFwxYtgL3xj0R3s7wbiFbqw= +github.com/Roshick/go-autumn-vault v0.1.1 h1:5aCrXL2KCC1wfSSaUmMzJ5+ueYxdTQNvEyOXnpC6S/E= +github.com/Roshick/go-autumn-vault v0.1.1/go.mod h1:tFn2gOr5/dxHGKClMpfGWp4bkBWvzUtvdDOs4INjj+I= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/StephanHCB/go-autumn-config-api v0.2.2 h1:A+DO0JT0dc/4e/UN6XwYCE7VNCXHL5Xlg8UrAqTbNXE= +github.com/StephanHCB/go-autumn-config-api v0.2.2/go.mod h1:6nJBwuT1uURHApOSFr6Rw+naK2YkO+sAduwEWZ0qsSU= +github.com/StephanHCB/go-autumn-logging v0.4.0 h1:/EC41JJBi1Ao8eFmx4jReokJsbKsRoMoGTaCJZ/Nins= +github.com/StephanHCB/go-autumn-logging v0.4.0/go.mod h1:dPABYdECU3XrFib03uXbQFVLftUP5c4YaKSineiw37U= +github.com/StephanHCB/go-autumn-restclient v0.9.1 h1:/5FeB826RC6ePIf76Q2IjjSUBoLqe1ubjlAHL/mTa9Y= +github.com/StephanHCB/go-autumn-restclient v0.9.1/go.mod h1:etWCMr0i0iAl1RVBgwLczoFt2rhWrUMySalot0i6vT8= +github.com/StephanHCB/go-autumn-web-swagger-ui v0.3.3 h1:09gZKOO8ZMDknikt1tRpx/5BEc37yp053Sc/R8zP3HI= +github.com/StephanHCB/go-autumn-web-swagger-ui v0.3.3/go.mod h1:EBtCQXF8JhoADnadezundz2xWEdFPmx2NUTgZYtUE0M= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +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/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= +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/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= +github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= +github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= +github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= +github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc= +github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbTO1lpcGSkU= +github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= +github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/elazarl/goproxy v1.2.1 h1:njjgvO6cRG9rIqN2ebkqy6cQz2Njkx7Fsfv/zIZqgug= +github.com/elazarl/goproxy v1.2.1/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64= +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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI= +github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= +github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= +github.com/go-chi/chi/v5 v5.2.0 h1:Aj1EtB0qR2Rdo2dG4O94RIU35w2lvQSj6BRA4+qwFL0= +github.com/go-chi/chi/v5 v5.2.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= +github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= +github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA= +github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= +github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E= +github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a h1:v6zMvHuY9yue4+QkG/HQ/W67wvtQmWJ4SDo9aK/GIno= +github.com/go-http-utils/headers v0.0.0-20181008091004-fed159eddc2a/go.mod h1:I79BieaU4fxrw4LMXby6q5OS9XnoR9UIKLOzDFjUmuw= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +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.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +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/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.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +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-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= +github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= +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/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +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/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= +github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +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/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= +github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY= +github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= +github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +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/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +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/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pjbgf/sha1cd v0.3.1 h1:Dh2GYdpJnO84lIw0LJwTFXjcNbasP/bklicSznyAaPI= +github.com/pjbgf/sha1cd v0.3.1/go.mod h1:Y8t7jSB/dEI/lQE04A1HVKteqjj9bX5O4+Cex0TCu8s= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/redis/rueidis v1.0.52 h1:VFlucmx/QHzGD7FYlK8l+W/HYz4aK+2eIOrCVjDwwag= +github.com/redis/rueidis v1.0.52/go.mod h1:by+34b0cFXndxtYmPAHpoTHO5NkosDlBvhexoTURIxM= +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/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= +github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs= +github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1qZoYLZzLnBw+QkPP9WZnjlSWihhxAJC1+/M= +github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92 h1:OfRzdxCzDhp+rsKWXuOO2I/quKMJ/+TQwVbIP/gltZg= +github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92/go.mod h1:7/OT02F6S6I7v6WXb+IjhMuZEYfH/RJ5RwEWnEo5BMg= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= +github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= +github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= +github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +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.1.1/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/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/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= +github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/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= +github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= +github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +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-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +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.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +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/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/validator.v2 v2.0.1 h1:xF0KWyGWXm/LM2G1TrEjqOu4pa6coO9AlWSf3msVfDY= +gopkg.in/validator.v2 v2.0.1/go.mod h1:lIUZBlB3Im4s/eYp39Ry/wkR02yOPhZ9IwIRBjuPuG8= +gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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.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= +helm.sh/helm/v3 v3.16.4 h1:rBn/h9MACw+QlhxQTjpl8Ifx+VTWaYsw3rguGBYBzr0= +helm.sh/helm/v3 v3.16.4/go.mod h1:k8QPotUt57wWbi90w3LNmg3/MWcLPigVv+0/X4B8BzA= +k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= +k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= +k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= +k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= +k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= +k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= +k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= +k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.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-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= +oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= +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/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo= +sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= +sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= +sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= +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.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/internal/config/application.go b/internal/config/application.go new file mode 100755 index 00000000..52e13b15 --- /dev/null +++ b/internal/config/application.go @@ -0,0 +1,242 @@ +package config + +import ( + "fmt" + "strconv" + + "helm.sh/helm/v3/pkg/getter" + + "github.com/Roshick/manifest-maestro/pkg/utils/stringutils" + auconfigapi "github.com/StephanHCB/go-autumn-config-api" +) + +const ( + keyApplicationName = "APPLICATION_NAME" + + keyServerAddress = "SERVER_ADDRESS" + keyServerPrimaryPort = "SERVER_PRIMARY_PORT" + + keySSHPrivateKey = "SSH_PRIVATE_KEY" + keySSHPrivateKeyPassword = "SSH_PRIVATE_KEY_PASSWORD" + + keyHelmDefaultReleaseName = "HELM_DEFAULT_RELEASE_NAME" + keyHelmDefaultKubernetesAPIVersions = "HELM_DEFAULT_KUBERNETES_API_VERSIONS" + keyHelmDefaultKubernetesNamespace = "HELM_DEFAULT_KUBERNETES_NAMESPACE" + + keySynchronizationMethod = "SYNCHRONIZATION_METHOD" + keySynchronizationRedisURL = "SYNCHRONIZATION_REDIS_URL" + keySynchronizationRedisPassword = "SYNCHRONIZATION_REDIS_PASSWORD" +) + +type SynchronizationMethod int64 + +const ( + SynchronizationMethodUnknown SynchronizationMethod = iota + SynchronizationMethodMemory + SynchronizationMethodRedis +) + +type ApplicationConfig struct { + vApplicationName string + + vServerAddress string + vServerPrimaryPort uint + vServerMetricsPort uint + + vSSHPrivateKey string + vSSHPrivateKeyPassword string + + vHelmDefaultReleaseName string + vHelmDefaultKubernetesAPIVersions []string + vHelmDefaultKubernetesNamespace string + + vSynchronizationMethod SynchronizationMethod + vSynchronizationRedisURL string + vSynchronizationRedisPassword string +} + +func NewApplicationConfig() *ApplicationConfig { + return &ApplicationConfig{} +} + +func (c *ApplicationConfig) ApplicationName() string { + return c.vApplicationName +} + +func (c *ApplicationConfig) ServerAddress() string { + return c.vServerAddress +} + +func (c *ApplicationConfig) ServerPrimaryPort() uint { + return c.vServerPrimaryPort +} + +func (c *ApplicationConfig) SSHPrivateKey() string { + return c.vSSHPrivateKey +} + +func (c *ApplicationConfig) SSHPrivateKeyPassword() string { + return c.vSSHPrivateKeyPassword +} + +func (c *ApplicationConfig) HelmProviders() []getter.Provider { + return []getter.Provider{ + { + Schemes: []string{"http", "https"}, + New: getter.NewHTTPGetter, + }, + } +} + +func (c *ApplicationConfig) HelmDefaultReleaseName() string { + return c.vHelmDefaultReleaseName +} + +func (c *ApplicationConfig) HelmDefaultKubernetesAPIVersions() []string { + return c.vHelmDefaultKubernetesAPIVersions +} + +func (c *ApplicationConfig) HelmDefaultKubernetesNamespace() string { + return c.vHelmDefaultKubernetesNamespace +} + +func (c *ApplicationConfig) SynchronizationMethod() SynchronizationMethod { + return c.vSynchronizationMethod +} + +func (c *ApplicationConfig) SynchronizationRedisURL() string { + return c.vSynchronizationRedisURL +} + +func (c *ApplicationConfig) SynchronizationRedisPassword() string { + return c.vSynchronizationRedisPassword +} + +func (c *ApplicationConfig) ConfigItems() []auconfigapi.ConfigItem { + return []auconfigapi.ConfigItem{ + { + Key: keyApplicationName, + EnvName: keyApplicationName, + Description: "Name of the application.", + Default: "manifest-maestro", + }, + { + Key: keyServerAddress, + EnvName: keyServerAddress, + Description: "Address all servers listen on.", + Default: "", + }, + { + Key: keyServerPrimaryPort, + EnvName: keyServerPrimaryPort, + Description: "Port used by the primary (i.e. application) server.", + Default: "8080", + }, + { + Key: keySSHPrivateKey, + EnvName: keySSHPrivateKey, + Description: "SSH private key used to access git repositories. Requires read permissions.", + Default: "", + }, + { + Key: keySSHPrivateKeyPassword, + EnvName: keySSHPrivateKeyPassword, + Description: "SSH private key password.", + Default: "", + }, + { + Key: keyHelmDefaultReleaseName, + EnvName: keyHelmDefaultReleaseName, + Description: "Default release name used by helm.", + Default: "RELEASE-NAME", + }, + { + Key: keyHelmDefaultKubernetesAPIVersions, + EnvName: keyHelmDefaultKubernetesAPIVersions, + Description: "Comma separated list of default API versions used by helm.", + Default: "", + }, + { + Key: keyHelmDefaultKubernetesNamespace, + EnvName: keyHelmDefaultKubernetesNamespace, + Description: "Default Kubernetes namespace used by helm.", + Default: "default", + }, + { + Key: keySynchronizationMethod, + EnvName: keySynchronizationMethod, + Default: "MEMORY", + Description: "Type of synchronization used between multiple instances of the service.", + }, + { + Key: keySynchronizationRedisURL, + EnvName: keySynchronizationRedisURL, + Default: "", + Description: "URL of the Redis instance used in case of synchronization method 'REDIS'.", + }, + { + Key: keySynchronizationRedisPassword, + EnvName: keySynchronizationRedisPassword, + Default: "", + Description: "Password to the Redis instance used in case of synchronization method 'REDIS'.", + }, + } +} + +func (c *ApplicationConfig) ObtainValues(getter func(string) string) error { + c.vApplicationName = getter(keyApplicationName) + + c.vServerAddress = getter(keyServerAddress) + vServerPrimaryPort, err := parseUint(getter(keyServerPrimaryPort)) + if err != nil { + return err + } + c.vServerPrimaryPort = vServerPrimaryPort + + c.vSSHPrivateKey = getter(keySSHPrivateKey) + c.vSSHPrivateKeyPassword = getter(keySSHPrivateKeyPassword) + + c.vHelmDefaultReleaseName = getter(keyHelmDefaultReleaseName) + c.vHelmDefaultKubernetesAPIVersions = stringutils.SplitUniqueNonEmpty(getter(keyHelmDefaultKubernetesAPIVersions), ",") + c.vHelmDefaultKubernetesNamespace = getter(keyHelmDefaultKubernetesNamespace) + + vSynchronizationMethod, err := parseSynchronizationMethod(getter(keySynchronizationMethod)) + if err != nil { + return err + } + c.vSynchronizationMethod = vSynchronizationMethod + c.vSynchronizationRedisURL = getter(keySynchronizationRedisURL) + c.vSynchronizationRedisPassword = getter(keySynchronizationRedisPassword) + + return nil +} + +func parseBoolean(value string) (bool, error) { + boolValue, err := strconv.ParseBool(value) + if err != nil { + return false, fmt.Errorf("value '%s' is not a valid boolean", value) + } + return boolValue, nil +} + +func parseUint(value string) (uint, error) { + intValue, err := strconv.Atoi(value) + if err != nil { + return 0, fmt.Errorf("value '%s' is not a valid integer: %w", value, err) + } + if intValue < 0 { + return 0, fmt.Errorf("value '%s' is not a valid unsigned integer", value) + } + return uint(intValue), nil +} + +func parseSynchronizationMethod(value string) (SynchronizationMethod, error) { + switch value { + case "REDIS": + return SynchronizationMethodRedis, nil + case "MEMORY": + return SynchronizationMethodMemory, nil + default: + return SynchronizationMethodUnknown, fmt.Errorf("invalid synchronization method: '%s'", value) + } +} diff --git a/internal/config/bootstrap.go b/internal/config/bootstrap.go new file mode 100755 index 00000000..817c68e3 --- /dev/null +++ b/internal/config/bootstrap.go @@ -0,0 +1,80 @@ +package config + +import ( + "fmt" + + auconfigapi "github.com/StephanHCB/go-autumn-config-api" +) + +type LogType int64 + +const ( + LogStylePlain LogType = iota + LogStyleJSON +) + +const ( + keyLogStyle = "LOG_STYLE" + + keyVaultEnabled = "VAULT_ENABLED" +) + +type BootstrapConfig struct { + vLogType LogType + + vVaultEnabled bool +} + +func NewBootstrapConfig() *BootstrapConfig { + return &BootstrapConfig{} +} + +func (c *BootstrapConfig) LogType() LogType { + return c.vLogType +} + +func (c *BootstrapConfig) VaultEnabled() bool { + return c.vVaultEnabled +} + +func (c *BootstrapConfig) ConfigItems() []auconfigapi.ConfigItem { + return []auconfigapi.ConfigItem{ + { + Key: keyLogStyle, + EnvName: keyLogStyle, + Default: "PLAIN", + Description: "", + }, { + Key: keyVaultEnabled, + EnvName: keyVaultEnabled, + Default: "false", + Description: "", + }, + } +} + +func (c *BootstrapConfig) ObtainValues(getter func(string) string) error { + if vLogType, err := parseLogType(getter(keyLogStyle)); err != nil { + return err + } else { + c.vLogType = vLogType + } + + if vVaultEnabled, err := parseBoolean(getter(keyVaultEnabled)); err != nil { + return err + } else { + c.vVaultEnabled = vVaultEnabled + } + return nil +} + +func parseLogType(value string) (LogType, error) { + switch value { + case "JSON": + return LogStyleJSON, nil + case "PLAIN": + return LogStylePlain, nil + default: + return LogStylePlain, fmt.Errorf("invalid log type: '%s'", value) + } +} diff --git a/internal/config/config.go b/internal/config/config.go new file mode 100755 index 00000000..ff4d1dba --- /dev/null +++ b/internal/config/config.go @@ -0,0 +1,38 @@ +package config + +import ( + "github.com/Roshick/go-autumn-vault/pkg/vault" +) + +const ( + ExitCodeSuccess = 0 + ExitCodeDirtyShutdown = 10 + ExitCodeCreateFailed = 20 + ExitCodeRunFailed = 30 +) + +func New() *Config { + return &Config{ + vBootstrap: NewBootstrapConfig(), + vVault: vault.NewDefaultConfig(), + vApplication: NewApplicationConfig(), + } +} + +type Config struct { + vBootstrap *BootstrapConfig + vVault *vault.DefaultConfigImpl + vApplication *ApplicationConfig +} + +func (c *Config) Bootstrap() *BootstrapConfig { + return c.vBootstrap +} + +func (c *Config) Vault() *vault.DefaultConfigImpl { + return c.vVault +} + +func (c *Config) Application() *ApplicationConfig { + return c.vApplication +} diff --git a/internal/repository/clock/clock.go b/internal/repository/clock/clock.go new file mode 100644 index 00000000..0b5a528d --- /dev/null +++ b/internal/repository/clock/clock.go @@ -0,0 +1,13 @@ +package clock + +import "time" + +type Clock struct{} + +func New() *Clock { + return &Clock{} +} + +func (c *Clock) Now() time.Time { + return time.Now() +} diff --git a/internal/repository/git/git.go b/internal/repository/git/git.go new file mode 100644 index 00000000..16110d1b --- /dev/null +++ b/internal/repository/git/git.go @@ -0,0 +1,83 @@ +package git + +import ( + "context" + "fmt" + + "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-git/v5/storage/memory" + + "github.com/Roshick/manifest-maestro/internal/config" + "github.com/go-git/go-git/v5" + gitConfig "github.com/go-git/go-git/v5/config" + "github.com/go-git/go-git/v5/plumbing" + "github.com/go-git/go-git/v5/plumbing/transport/ssh" +) + +type Git struct { + sshAuth *ssh.PublicKeys +} + +func New(configuration *config.ApplicationConfig) (*Git, error) { + var sshAuth *ssh.PublicKeys + if configuration.SSHPrivateKey() != "" { + var err error + sshAuth, err = ssh.NewPublicKeys("git", + []byte(configuration.SSHPrivateKey()), configuration.SSHPrivateKeyPassword()) + if err != nil { + return nil, err + } + } + + return &Git{ + sshAuth: sshAuth, + }, nil +} + +func (r *Git) FetchReferences(_ context.Context, repoURL string) ([]*plumbing.Reference, error) { + rem := git.NewRemote(memory.NewStorage(), &gitConfig.RemoteConfig{ + Name: "origin", + URLs: []string{repoURL}, + }) + + return rem.List(&git.ListOptions{ + Auth: r.sshAuth, + }) +} + +func (r *Git) CloneCommit(_ context.Context, gitURL string, gitReferenceOrHash string) (*git.Repository, error) { + repo, err := git.Init(memory.NewStorage(), memfs.New()) + if err != nil { + return nil, err + } + + if _, err = repo.CreateRemote(&gitConfig.RemoteConfig{ + Name: "origin", + URLs: []string{gitURL}, + }); err != nil { + return nil, err + } + + localBranch := "refs/heads/local" + refSpec := fmt.Sprintf("%s:%s", gitReferenceOrHash, localBranch) + if err = repo.Fetch(&git.FetchOptions{ + Auth: r.sshAuth, + RefSpecs: []gitConfig.RefSpec{gitConfig.RefSpec(refSpec)}, + Depth: 1, + }); err != nil { + return nil, err + } + + tree, err := repo.Worktree() + if err != nil { + return nil, err + } + + if err = tree.Checkout(&git.CheckoutOptions{ + Branch: plumbing.ReferenceName(localBranch), + }); err != nil { + return nil, err + } + + return repo, nil +} diff --git a/internal/repository/helmremote/helmremote.go b/internal/repository/helmremote/helmremote.go new file mode 100644 index 00000000..6422191f --- /dev/null +++ b/internal/repository/helmremote/helmremote.go @@ -0,0 +1,84 @@ +package helmremote + +import ( + "context" + "io" + + "github.com/Roshick/manifest-maestro/internal/config" + "github.com/google/uuid" + "helm.sh/helm/v3/pkg/downloader" + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/repo" +) + +type HelmRemote struct { + appConfig config.ApplicationConfig +} + +func New() *HelmRemote { + return &HelmRemote{} +} + +func (r *HelmRemote) GetIndex( + _ context.Context, + repositoryURL string, +) ([]byte, error) { + c := repo.Entry{ + URL: repositoryURL, + Name: uuid.NewString(), + } + + chartRepository, err := repo.NewChartRepository(&c, r.appConfig.HelmProviders()) + if err != nil { + return nil, err + } + + indexURL, err := repo.ResolveReferenceURL(chartRepository.Config.URL, "index.yaml") + if err != nil { + return nil, err + } + + resp, err := chartRepository.Client.Get(indexURL, + getter.WithURL(chartRepository.Config.URL), + getter.WithInsecureSkipVerifyTLS(chartRepository.Config.InsecureSkipTLSverify), + getter.WithTLSClientConfig(chartRepository.Config.CertFile, chartRepository.Config.KeyFile, chartRepository.Config.CAFile), + getter.WithBasicAuth(chartRepository.Config.Username, chartRepository.Config.Password), + getter.WithPassCredentialsAll(chartRepository.Config.PassCredentialsAll), + ) + if err != nil { + return nil, err + } + return io.ReadAll(resp) +} + +func (r *HelmRemote) GetChart( + _ context.Context, + chartRef string, +) ([]byte, error) { + chartDownloader := downloader.ChartDownloader{ + Out: r, + Verify: downloader.VerifyNever, + Getters: r.appConfig.HelmProviders(), + } + + chartURL, err := chartDownloader.ResolveChartVersion(chartRef, "") + if err != nil { + return nil, err + } + + urlGetter, err := chartDownloader.Getters.ByScheme(chartURL.Scheme) + if err != nil { + return nil, err + } + + chartBuffer, err := urlGetter.Get(chartURL.String(), chartDownloader.Options...) + if err != nil { + return nil, err + } + + return chartBuffer.Bytes(), nil +} + +func (r *HelmRemote) Write(_ []byte) (int, error) { + return 0, nil +} diff --git a/internal/service/cache/error.go b/internal/service/cache/error.go new file mode 100644 index 00000000..08bf029d --- /dev/null +++ b/internal/service/cache/error.go @@ -0,0 +1 @@ +package cache diff --git a/internal/service/cache/gitrepositorycache.go b/internal/service/cache/gitrepositorycache.go new file mode 100644 index 00000000..148ea3ef --- /dev/null +++ b/internal/service/cache/gitrepositorycache.go @@ -0,0 +1,143 @@ +package cache + +import ( + "bytes" + "context" + "fmt" + "regexp" + "strings" + "time" + + "github.com/Roshick/go-autumn-synchronisation/pkg/cache" + "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" + "github.com/Roshick/manifest-maestro/pkg/utils/targz" + aulogging "github.com/StephanHCB/go-autumn-logging" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing" +) + +type Git interface { + FetchReferences(context.Context, string) ([]*plumbing.Reference, error) + + CloneCommit(context.Context, string, string) (*git.Repository, error) +} + +type GitRepositoryCache struct { + git Git + cache cache.Cache[[]byte] + + commitHashRegex *regexp.Regexp +} + +func NewGitRepositoryCache(git Git, cache cache.Cache[[]byte]) *GitRepositoryCache { + return &GitRepositoryCache{ + git: git, + cache: cache, + commitHashRegex: regexp.MustCompile("[[:xdigit:]]{40}"), + } +} + +func (c *GitRepositoryCache) RetrieveRepository(ctx context.Context, url string, referenceOrHash string) ([]byte, error) { + hash, err := c.toHash(ctx, url, referenceOrHash) + if err != nil { + return nil, err + } + + key := c.cacheKey(url, hash) + cached, err := c.cache.Get(ctx, key) + if err != nil { + return nil, err + } + if cached != nil { + aulogging.Logger.Ctx(ctx).Info().Printf("cache hit for git repository with key '%s'", key) + return *cached, nil + } + aulogging.Logger.Ctx(ctx).Info().Printf("cache miss for git repository with key '%s', retrieving from remote", key) + return c.RefreshRepository(ctx, url, hash) +} + +func (c *GitRepositoryCache) RetrieveRepositoryToFileSystem( + ctx context.Context, url string, referenceOrHash string, fileSystem *filesystem.FileSystem, +) error { + tarball, err := c.RetrieveRepository(ctx, url, referenceOrHash) + if err != nil { + return err + } + if err = targz.Extract(ctx, fileSystem, bytes.NewBuffer(tarball), fileSystem.Root); err != nil { + return err + } + return nil +} + +func (c *GitRepositoryCache) RefreshRepository(ctx context.Context, url string, reference string) ([]byte, error) { + hash, err := c.toHash(ctx, url, reference) + if err != nil { + return nil, err + } + + tarball, err := c.fetchAsTarball(ctx, url, hash) + key := c.cacheKey(url, hash) + if err = c.cache.Set(ctx, key, tarball, time.Hour); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("failed to cache git repository with key '%s'", key) + } else { + aulogging.Logger.Ctx(ctx).Info().Printf("successfully cached git repository with key '%s'", key) + } + + return tarball, nil +} + +func (c *GitRepositoryCache) toHash(ctx context.Context, url string, referenceOrHash string) (string, error) { + if !c.isFullyQualifiedReference(referenceOrHash) && !c.isCommitHash(referenceOrHash) { + return "", fmt.Errorf("ToDo invalid reference %s", referenceOrHash) + } + + if c.isCommitHash(referenceOrHash) { + return referenceOrHash, nil + } + + remoteReferences, err := c.git.FetchReferences(ctx, url) + if err != nil { + return "", err + } + for _, ref := range remoteReferences { + if ref.Name().String() == referenceOrHash { + return ref.Hash().String(), nil + } + } + return "", fmt.Errorf("ToDo: reference %q not found", referenceOrHash) +} + +func (c *GitRepositoryCache) fetchAsTarball(ctx context.Context, url string, hash string) ([]byte, error) { + repo, err := c.git.CloneCommit(ctx, url, hash) + if err != nil { + return nil, err + } + tree, err := repo.Worktree() + if err != nil { + return nil, err + } + + fileSystem := filesystem.New() + if err = filesystem.CopyFromBillyToFileSystem(tree.Filesystem, fileSystem); err != nil { + return nil, err + } + repoBuffer := new(bytes.Buffer) + if err = targz.Compress(ctx, fileSystem, fileSystem.Root, "", repoBuffer); err != nil { + return nil, err + } + return repoBuffer.Bytes(), nil +} + +func (c *GitRepositoryCache) isFullyQualifiedReference(reference string) bool { + return reference == "HEAD" || + strings.HasPrefix(reference, "refs/heads/") || + strings.HasPrefix(reference, "refs/tags/") +} + +func (c *GitRepositoryCache) isCommitHash(reference string) bool { + return c.commitHashRegex.MatchString(reference) +} + +func (c *GitRepositoryCache) cacheKey(gitURL string, gitReference string) string { + return fmt.Sprintf("%s|%s", gitURL, gitReference) +} diff --git a/internal/service/cache/helmchartcache.go b/internal/service/cache/helmchartcache.go new file mode 100644 index 00000000..9357e4fc --- /dev/null +++ b/internal/service/cache/helmchartcache.go @@ -0,0 +1,98 @@ +package cache + +import ( + "bytes" + "context" + "fmt" + "github.com/Roshick/go-autumn-synchronisation/pkg/cache" + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/Roshick/manifest-maestro/pkg/utils/commonutils" + "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" + "github.com/Roshick/manifest-maestro/pkg/utils/targz" + aulogging "github.com/StephanHCB/go-autumn-logging" + "helm.sh/helm/v3/pkg/repo" + "strings" + "time" +) + +type HelmChartRemote interface { + GetChart(context.Context, string) ([]byte, error) +} + +type HelmChartCache struct { + helmRemote HelmChartRemote + indexCache *HelmIndexCache + cache cache.Cache[[]byte] +} + +func NewHelmChartCache(helmRemote HelmChartRemote, indexCache *HelmIndexCache, cache cache.Cache[[]byte]) *HelmChartCache { + return &HelmChartCache{ + helmRemote: helmRemote, + indexCache: indexCache, + cache: cache, + } +} + +func (c *HelmChartCache) RetrieveChart(ctx context.Context, reference api.HelmChartRepositoryChartReference) ([]byte, error) { + index, err := c.indexCache.RetrieveIndex(ctx, reference.RepositoryURL) + if err != nil { + return nil, err + } + + // ToDo: Maybe retry with refreshed index cache on error or empty urls + chartVersion, err := index.Get(reference.ChartName, commonutils.DefaultIfNil(reference.ChartVersion, "")) + if err != nil { + return nil, err + } + if len(chartVersion.URLs) == 0 { + return nil, fmt.Errorf("failed to find downloadable chart version in index") + } + + key := c.cacheKey(reference.RepositoryURL, chartVersion.Name, chartVersion.Version, chartVersion.Digest) + cached, err := c.cache.Get(ctx, key) + if err != nil { + return nil, err + } + if cached != nil { + aulogging.Logger.Ctx(ctx).Info().Printf("cache hit for helm chart with key '%s'", key) + return *cached, nil + } + aulogging.Logger.Ctx(ctx).Info().Printf("cache miss for helm chart with key '%s', retrieving from remote", key) + return c.RefreshChart(ctx, reference.RepositoryURL, chartVersion) +} + +func (c *HelmChartCache) RetrieveChartToFileSystem(ctx context.Context, reference api.HelmChartRepositoryChartReference, fileSystem *filesystem.FileSystem) error { + tarball, err := c.RetrieveChart(ctx, reference) + if err != nil { + return err + } + if err = targz.Extract(ctx, fileSystem, bytes.NewBuffer(tarball), fileSystem.Root); err != nil { + return err + } + return nil +} + +func (c *HelmChartCache) RefreshChart(ctx context.Context, repositoryURL string, chartVersion *repo.ChartVersion) ([]byte, error) { + chartURL := chartVersion.URLs[0] + // no protocol => url is relative + if !strings.Contains(chartURL, "://") { + chartURL = fmt.Sprintf("%s/%s", repositoryURL, chartURL) + } + chartBytes, err := c.helmRemote.GetChart(ctx, chartURL) + if err != nil { + return nil, err + } + + key := c.cacheKey(repositoryURL, chartVersion.Name, chartVersion.Version, chartVersion.Digest) + if err = c.cache.Set(ctx, key, chartBytes, 12*time.Hour); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("failed to cache helm chart with key '%s'", key) + } else { + aulogging.Logger.Ctx(ctx).Info().Printf("successfully cached helm chart with key '%s'", key) + } + + return chartBytes, nil +} + +func (c *HelmChartCache) cacheKey(repositoryURL string, chartName string, version string, chartDigest string) string { + return fmt.Sprintf("%s|%s|%s|%s", repositoryURL, chartName, version, chartDigest) +} diff --git a/internal/service/cache/helmindexcache.go b/internal/service/cache/helmindexcache.go new file mode 100644 index 00000000..a25fe694 --- /dev/null +++ b/internal/service/cache/helmindexcache.go @@ -0,0 +1,94 @@ +package cache + +import ( + "context" + "encoding/json" + "github.com/Roshick/go-autumn-synchronisation/pkg/cache" + aulogging "github.com/StephanHCB/go-autumn-logging" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/repo" + "sigs.k8s.io/yaml" + "time" +) + +type HelmIndexRemote interface { + GetIndex(context.Context, string) ([]byte, error) +} + +type HelmIndexCache struct { + helmRemote HelmIndexRemote + cache cache.Cache[[]byte] +} + +func NewHelmIndexCache(helmRemote HelmIndexRemote, cache cache.Cache[[]byte]) *HelmIndexCache { + return &HelmIndexCache{ + helmRemote: helmRemote, + cache: cache, + } +} + +func (c *HelmIndexCache) RetrieveIndex(ctx context.Context, repositoryURL string) (*repo.IndexFile, error) { + cached, err := c.cache.Get(ctx, repositoryURL) + if err != nil { + return nil, err + } + if cached != nil { + aulogging.Logger.Ctx(ctx).Info().Printf("cache hit for helm repository index with key '%s'", repositoryURL) + return c.parseIndex(*cached) + } + aulogging.Logger.Ctx(ctx).Info().Printf("cache miss for helm repository index with key '%s', retrieving from remote", repositoryURL) + return c.RefreshIndex(ctx, repositoryURL) +} + +func (c *HelmIndexCache) RefreshIndex(ctx context.Context, repositoryURL string) (*repo.IndexFile, error) { + indexBytes, err := c.helmRemote.GetIndex(ctx, repositoryURL) + if err != nil { + return nil, err + } + + if err = c.cache.Set(ctx, repositoryURL, indexBytes, 3*time.Minute); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("failed to cache helm repository index with key '%s'", repositoryURL) + } else { + aulogging.Logger.Ctx(ctx).Info().Printf("successfully cached helm repository index with key '%s'", repositoryURL) + } + + return c.parseIndex(indexBytes) +} + +func (c *HelmIndexCache) parseIndex(data []byte) (*repo.IndexFile, error) { + index := &repo.IndexFile{} + + if len(data) == 0 { + return nil, repo.ErrEmptyIndexYaml + } + if err := c.jsonOrYamlUnmarshal(data, index); err != nil { + return nil, err + } + if index.APIVersion == "" { + return index, repo.ErrNoAPIVersion + } + + for _, cvs := range index.Entries { + for idx := len(cvs) - 1; idx >= 0; idx-- { + if cvs[idx] == nil { + continue + } + if cvs[idx].APIVersion == "" { + cvs[idx].APIVersion = chart.APIVersionV1 + } + if err := cvs[idx].Validate(); err != nil { + cvs = append(cvs[:idx], cvs[idx+1:]...) + } + } + } + index.SortEntries() + + return index, nil +} + +func (c *HelmIndexCache) jsonOrYamlUnmarshal(unknownBytes []byte, obj any) error { + if json.Valid(unknownBytes) { + return json.Unmarshal(unknownBytes, obj) + } + return yaml.UnmarshalStrict(unknownBytes, obj) +} diff --git a/internal/service/helm/chart.go b/internal/service/helm/chart.go new file mode 100644 index 00000000..aaa5e127 --- /dev/null +++ b/internal/service/helm/chart.go @@ -0,0 +1,67 @@ +package helm + +import ( + "fmt" + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" + "github.com/Roshick/manifest-maestro/pkg/utils/maputils" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/strvals" + "sigs.k8s.io/yaml" +) + +type Chart struct { + chart *chart.Chart + fileSystem *filesystem.FileSystem + targetPath string +} + +func (c *Chart) DefaultValues() map[string]any { + return c.chart.Values +} + +func (c *Chart) MergeValues(parameters api.HelmRenderParameters) (map[string]any, error) { + values := maputils.DeepMerge(parameters.ComplexValues, make(map[string]any)) + + for _, fileName := range parameters.ValueFiles { + filePath := c.fileSystem.Join(c.targetPath, fileName) + if c.fileSystem.Exists(filePath) { + valueFile, err := c.fileSystem.ReadFile(filePath) + if err != nil { + return nil, err + } + tmpValues := make(map[string]any) + if err = yaml.Unmarshal(valueFile, &tmpValues); err != nil { + return nil, err + } + values = maputils.DeepMerge(tmpValues, values) + } else if parameters.IgnoreMissingValueFiles == nil || !*parameters.IgnoreMissingValueFiles { + return nil, fmt.Errorf(fmt.Sprintf("repository is missing value file at '%s'", filePath)) + } + } + + for _, value := range append(c.flattenValues(parameters.Values), parameters.ValuesFlat...) { + if err := strvals.ParseInto(value, values); err != nil { + return nil, err + } + } + + for _, value := range append(c.flattenValues(parameters.StringValues), parameters.StringValuesFlat...) { + if err := strvals.ParseIntoString(value, values); err != nil { + return nil, err + } + } + + return values, nil +} + +func (c *Chart) flattenValues(values map[string]string) []string { + if values == nil { + return nil + } + flattenedValues := make([]string, 0) + for key, value := range values { + flattenedValues = append(flattenedValues, fmt.Sprintf("%s=%s", key, value)) + } + return flattenedValues +} diff --git a/internal/service/helm/chartprovider.go b/internal/service/helm/chartprovider.go new file mode 100644 index 00000000..a28135a4 --- /dev/null +++ b/internal/service/helm/chartprovider.go @@ -0,0 +1,206 @@ +package helm + +import ( + "bytes" + "context" + "fmt" + "github.com/Roshick/manifest-maestro/internal/service/cache" + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/Roshick/manifest-maestro/pkg/utils/commonutils" + "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" + aulogging "github.com/StephanHCB/go-autumn-logging" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/ignore" + "io/fs" + "path/filepath" + "strings" +) + +const ( + chartsDir = "charts" +) + +type ChartProvider struct { + helmChartCache *cache.HelmChartCache + gitRepositoryCache *cache.GitRepositoryCache +} + +func NewChartProvider(helmChartCache *cache.HelmChartCache, gitRepositoryCache *cache.GitRepositoryCache) *ChartProvider { + return &ChartProvider{ + helmChartCache: helmChartCache, + gitRepositoryCache: gitRepositoryCache, + } +} + +func (p *ChartProvider) ListHelmCharts(ctx context.Context, abstractReference api.HelmRepositoryReference) ([]map[string]any, error) { + if reference := abstractReference.HelmChartRepositoryReference; reference != nil { + // ToDo + return nil, nil + } + return nil, fmt.Errorf("ToDo") +} + +func (p *ChartProvider) GetHelmChart(ctx context.Context, abstractReference api.HelmChartReference) (*Chart, error) { + if reference := abstractReference.HelmChartRepositoryChartReference; reference != nil { + return p.getHelmChartFromHelmRepositoryChartReference(ctx, *reference) + } + if reference := abstractReference.GitRepositoryPathReference; reference != nil { + return p.getHelmChartFromGitRepositoryPathReference(ctx, *reference) + } + return nil, fmt.Errorf("ToDo") +} + +func (p *ChartProvider) getHelmChartFromGitRepositoryPathReference(ctx context.Context, reference api.GitRepositoryPathReference) (*Chart, error) { + fileSystem := filesystem.New() + + targetPath := fileSystem.Root + if !commonutils.IsEmpty(reference.Path) { + if fileSystem.IsAbs(*reference.Path) { + return nil, fmt.Errorf("git source path cannot be absolute") + } + targetPath = fileSystem.Join(targetPath, *reference.Path) + } + + err := p.gitRepositoryCache.RetrieveRepositoryToFileSystem(ctx, reference.RepositoryURL, reference.GitReference, fileSystem) + if err != nil { + return nil, err + } + + return p.buildChart(ctx, fileSystem, targetPath) +} + +func (p *ChartProvider) getHelmChartFromHelmRepositoryChartReference(ctx context.Context, reference api.HelmChartRepositoryChartReference) (*Chart, error) { + fileSystem := filesystem.New() + + err := p.helmChartCache.RetrieveChartToFileSystem(ctx, reference, fileSystem) + if err != nil { + return nil, err + } + + targetPath := fileSystem.Join(fileSystem.Root, reference.ChartName) + return p.buildChart(ctx, fileSystem, targetPath) +} + +func (p *ChartProvider) buildChart(ctx context.Context, fileSystem *filesystem.FileSystem, targetPath string) (*Chart, error) { + aulogging.Logger.Ctx(ctx).Info().Printf("building chart at %s", targetPath) + + helmChart, err := p.loadChart(ctx, fileSystem, targetPath) + if err != nil { + return nil, err + } + + chartsPath := fileSystem.Join(targetPath, chartsDir) + if err = fileSystem.MkdirAll(chartsPath); err != nil { + return nil, err + } + + for _, dependency := range helmChart.Metadata.Dependencies { + if innerErr := dependency.Validate(); innerErr != nil { + return nil, innerErr + } + + if path := fileSystem.Join(chartsPath, fmt.Sprintf("%s-%s.tgz", dependency.Name, dependency.Version)); fileSystem.Exists(path) { + dependencyChart, innerErr := p.loadChart(ctx, fileSystem, path) + if innerErr != nil { + return nil, innerErr + } + if dependencyChart.Metadata.Version == dependency.Version { + helmChart.AddDependency(dependencyChart) + continue + } + } else if path = fileSystem.Join(chartsPath, fmt.Sprintf("%s", dependency.Name)); fileSystem.Exists(path) { + dependencyChart, innerErr := p.loadChart(ctx, fileSystem, path) + if innerErr != nil { + return nil, innerErr + } + if dependencyChart.Metadata.Version == dependency.Version { + helmChart.AddDependency(dependencyChart) + continue + } + } + chartBytes, innerErr := p.helmChartCache.RetrieveChart(ctx, api.HelmChartRepositoryChartReference{ + RepositoryURL: dependency.Repository, + ChartName: dependency.Name, + ChartVersion: commonutils.Ptr(dependency.Version), + }) + if innerErr != nil { + return nil, innerErr + } + dependencyChart, innerErr := loader.LoadArchive(bytes.NewReader(chartBytes)) + if innerErr != nil { + return nil, innerErr + } + helmChart.AddDependency(dependencyChart) + } + + return &Chart{ + chart: helmChart, + fileSystem: fileSystem, + targetPath: targetPath, + }, nil +} + +func (p *ChartProvider) loadChart(ctx context.Context, fileSystem *filesystem.FileSystem, targetPath string) (*chart.Chart, error) { + if !fileSystem.IsAbs(targetPath) { + return nil, fmt.Errorf("ToDo: only absolute paths allowed") + } + + rules := ignore.Empty() + ignoreFilePath := fileSystem.Join(targetPath, ignore.HelmIgnore) + if fileSystem.Exists(ignoreFilePath) { + ignoreFile, err := fileSystem.Open(ignoreFilePath) + if err != nil { + return nil, err + } + defer func() { + if innerErr := ignoreFile.Close(); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(innerErr).Printf("failed to close '%s'", ignoreFilePath) + } + }() + rules, err = ignore.Parse(ignoreFile) + if err != nil { + return nil, err + } + } + rules.AddDefaults() + + files := make([]*loader.BufferedFile, 0) + walk := func(filePath string, fileInfo fs.FileInfo, err error) error { + if err != nil { + return err + } + if !fileInfo.Mode().IsRegular() { + return fmt.Errorf("cannot load irregular file '%s'", filePath) + } + + newFilePath := strings.TrimPrefix(filePath, targetPath) + newFilePath = strings.TrimPrefix(newFilePath, fileSystem.Separator) + if newFilePath == "" { + return nil + } + + if fileInfo.IsDir() { + if rules.Ignore(newFilePath, fileInfo) { + return fileSystem.SkipDir() + } + return nil + } + if rules.Ignore(newFilePath, fileInfo) { + return nil + } + + data, err := fileSystem.ReadFile(filePath) + if err != nil { + return fmt.Errorf("error reading %s: %w", newFilePath, err) + } + files = append(files, &loader.BufferedFile{Name: filepath.ToSlash(newFilePath), Data: data}) + return nil + } + + if err := fileSystem.Walk(targetPath, walk); err != nil { + return nil, err + } + + return loader.LoadFiles(files) +} diff --git a/internal/service/helm/chartrenderer.go b/internal/service/helm/chartrenderer.go new file mode 100644 index 00000000..c00e4315 --- /dev/null +++ b/internal/service/helm/chartrenderer.go @@ -0,0 +1,126 @@ +package helm + +import ( + "context" + "fmt" + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/Roshick/manifest-maestro/pkg/utils/commonutils" + "github.com/mitchellh/copystructure" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/engine" + "helm.sh/helm/v3/pkg/releaseutil" + "sigs.k8s.io/yaml" + "strings" +) + +const ( + defaultReleaseName = "RELEASE-NAME" + defaultNamespace = "default" +) + +type ChartRenderer struct { + defaultKubernetesAPIVersions []string +} + +func NewChartRenderer(defaultKubernetesAPIVersions []string) *ChartRenderer { + return &ChartRenderer{ + defaultKubernetesAPIVersions: defaultKubernetesAPIVersions, + } +} + +func (r *ChartRenderer) Render(_ context.Context, helmChart *Chart, parameters *api.HelmRenderParameters) ([]api.Manifest, *api.HelmRenderMetadata, error) { + actualParameters := api.HelmRenderParameters{} + if parameters != nil { + actualParameters = *parameters + } + + allValues, err := helmChart.MergeValues(actualParameters) + if err != nil { + return nil, nil, err + } + + if err = chartutil.ProcessDependencies(helmChart.chart, allValues); err != nil { + return nil, nil, err + } + + options := chartutil.ReleaseOptions{ + Name: commonutils.DefaultIfEmpty(actualParameters.ReleaseName, defaultReleaseName), + Namespace: commonutils.DefaultIfEmpty(actualParameters.Namespace, defaultNamespace), + } + + capabilities := chartutil.DefaultCapabilities.Copy() + capabilities.APIVersions = append(capabilities.APIVersions, r.defaultKubernetesAPIVersions...) + capabilities.APIVersions = append(capabilities.APIVersions, actualParameters.ApiVersions...) + renderValues, err := chartutil.ToRenderValues(helmChart.chart, allValues, options, capabilities) + if err != nil { + if strings.HasPrefix(err.Error(), "values don't meet the specifications of the schema(s)") { + return nil, nil, fmt.Errorf("ToDo: VALIDATION ERROR: %w", err) + } + return nil, nil, fmt.Errorf("ToDo: customize errors: %w", err) + } + + var mergedValues map[string]any + if values, ok := renderValues.AsMap()["Values"]; ok { + if typedValues, innerOk := values.(chartutil.Values); innerOk { + valuesCopy, innerErr := copystructure.Copy(typedValues) + if innerErr != nil { + return nil, nil, fmt.Errorf("ToDo: customize errors: %w", err) + } + mergedValues = valuesCopy.(chartutil.Values).AsMap() + } + } + metadata := &api.HelmRenderMetadata{ + ReleaseName: options.Name, + Namespace: options.Namespace, + ApiVersions: capabilities.APIVersions, + HelmVersion: capabilities.HelmVersion.Version, + MergedValues: mergedValues, + } + + var renderEngine engine.Engine + files, err := renderEngine.Render(helmChart.chart, renderValues) + if err != nil { + return nil, nil, fmt.Errorf("ToDo: customize errors") + } + if commonutils.DefaultIfNil(actualParameters.IncludeCRDs, true) { + for _, crd := range helmChart.chart.CRDObjects() { + files[crd.Filename] = string(crd.File.Data) + } + } + templateFiles := make(map[string]string) + for key, value := range files { + if (strings.HasSuffix(key, ".yaml") || strings.HasSuffix(key, ".yml")) && value != "" { + templateFiles[key] = value + } + } + + hooks, manifests, err := releaseutil.SortManifests(templateFiles, capabilities.APIVersions, releaseutil.InstallOrder) + if err != nil { + return nil, nil, err + } + parsedManifests := make([]api.Manifest, 0) + for _, manifest := range manifests { + parsedContent := make(map[string]any) + if err = yaml.Unmarshal([]byte(manifest.Content), &parsedContent); err != nil { + return nil, nil, err + } + parsedManifests = append(parsedManifests, api.Manifest{ + Source: commonutils.Ptr(manifest.Name), + Content: parsedContent, + }) + } + if commonutils.DefaultIfNil(actualParameters.IncludeHooks, true) { + for _, hook := range hooks { + parsedContent := make(map[string]any) + if err = yaml.Unmarshal([]byte(hook.Manifest), &parsedContent); err != nil { + return nil, nil, err + } + parsedManifests = append(parsedManifests, api.Manifest{ + Source: commonutils.Ptr(hook.Name), + Content: parsedContent, + }) + } + } + + return parsedManifests, metadata, nil +} diff --git a/internal/service/helm/error.go b/internal/service/helm/error.go new file mode 100644 index 00000000..9a90ecfa --- /dev/null +++ b/internal/service/helm/error.go @@ -0,0 +1 @@ +package helm diff --git a/internal/service/kustomize/error.go b/internal/service/kustomize/error.go new file mode 100644 index 00000000..e6abfbf2 --- /dev/null +++ b/internal/service/kustomize/error.go @@ -0,0 +1 @@ +package kustomize diff --git a/internal/service/kustomize/kustomization.go b/internal/service/kustomize/kustomization.go new file mode 100644 index 00000000..52223f0a --- /dev/null +++ b/internal/service/kustomize/kustomization.go @@ -0,0 +1,8 @@ +package kustomize + +import "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" + +type Kustomization struct { + fileSystem *filesystem.FileSystem + targetPath string +} diff --git a/internal/service/kustomize/kustomizationprovider.go b/internal/service/kustomize/kustomizationprovider.go new file mode 100644 index 00000000..0fc9a294 --- /dev/null +++ b/internal/service/kustomize/kustomizationprovider.go @@ -0,0 +1,53 @@ +package kustomize + +import ( + "context" + "fmt" + "github.com/Roshick/manifest-maestro/internal/service/cache" + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/Roshick/manifest-maestro/pkg/utils/commonutils" + "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" +) + +type KustomizationProvider struct { + gitRepositoryCache *cache.GitRepositoryCache +} + +func NewKustomizationProvider(gitRepositoryCache *cache.GitRepositoryCache) *KustomizationProvider { + return &KustomizationProvider{ + gitRepositoryCache: gitRepositoryCache, + } +} + +func (p *KustomizationProvider) GetKustomization(ctx context.Context, abstractReference api.KustomizationReference) (*Kustomization, error) { + if reference := abstractReference.GitRepositoryPathReference; reference != nil { + return p.getKustomizationFromGitRepositoryPathReference(ctx, *reference) + } + return nil, fmt.Errorf("ToDo") +} + +func (p *KustomizationProvider) getKustomizationFromGitRepositoryPathReference(ctx context.Context, reference api.GitRepositoryPathReference) (*Kustomization, error) { + fileSystem := filesystem.New() + + targetPath := fileSystem.Root + if !commonutils.IsEmpty(reference.Path) { + if fileSystem.IsAbs(*reference.Path) { + return nil, fmt.Errorf("git source path cannot be absolute") + } + targetPath = fileSystem.Join(targetPath, *reference.Path) + } + + err := p.gitRepositoryCache.RetrieveRepositoryToFileSystem(ctx, reference.RepositoryURL, reference.GitReference, fileSystem) + if err != nil { + return nil, err + } + + return p.buildKustomization(ctx, fileSystem, targetPath) +} + +func (p *KustomizationProvider) buildKustomization(_ context.Context, fileSystem *filesystem.FileSystem, targetPath string) (*Kustomization, error) { + return &Kustomization{ + fileSystem: fileSystem, + targetPath: targetPath, + }, nil +} diff --git a/internal/service/kustomize/kustomizationrenderer.go b/internal/service/kustomize/kustomizationrenderer.go new file mode 100644 index 00000000..5928c782 --- /dev/null +++ b/internal/service/kustomize/kustomizationrenderer.go @@ -0,0 +1,68 @@ +package kustomize + +import ( + "context" + "fmt" + "strings" + + "github.com/Roshick/manifest-maestro/pkg/api" + "sigs.k8s.io/kustomize/api/krusty" + "sigs.k8s.io/yaml" +) + +type KustomizationRenderer struct{} + +func NewKustomizationRenderer() *KustomizationRenderer { + return &KustomizationRenderer{} +} + +func (k *KustomizationRenderer) Render(_ context.Context, kustomization *Kustomization, parameters *api.KustomizeRenderParameters) ([]api.Manifest, error) { + kustomizer := krusty.MakeKustomizer(krusty.MakeDefaultOptions()) + + for _, injection := range parameters.ManifestInjections { + if injection.FileName == "" { + return nil, fmt.Errorf("ToDo: filename cannot be empty") + } + if strings.Contains(injection.FileName, kustomization.fileSystem.Separator) { + return nil, fmt.Errorf("ToDo: filename cannot contain %s", kustomization.fileSystem.Separator) + } + + yamlDocs := make([]string, 0) + for _, manifest := range injection.Manifests { + yamlBytes, innerErr := yaml.Marshal(manifest) + if innerErr != nil { + return nil, innerErr + } + yamlDocs = append(yamlDocs, string(yamlBytes)) + } + fileContent := []byte(strings.Join(yamlDocs, "---")) + + if err := kustomization.fileSystem.WriteFile(kustomization.fileSystem.Join(kustomization.targetPath, injection.FileName), fileContent); err != nil { + return nil, err + } + } + + manifests, err := kustomizer.Run(kustomization.fileSystem, kustomization.targetPath) + if err != nil { + err = fmt.Errorf("ToDo: %w", err) + return nil, err + } + + parsedManifests := make([]api.Manifest, 0) + for _, content := range manifests.Resources() { + contentBytes, innerErr := content.AsYAML() + if innerErr != nil { + return nil, innerErr + } + + parsedContent := make(map[string]any) + if innerErr = yaml.Unmarshal(contentBytes, &parsedContent); innerErr != nil { + return nil, innerErr + } + + parsedManifests = append(parsedManifests, api.Manifest{ + Content: parsedContent, + }) + } + return parsedManifests, nil +} diff --git a/internal/web/controller/health/health.go b/internal/web/controller/health/health.go new file mode 100644 index 00000000..b3c95f95 --- /dev/null +++ b/internal/web/controller/health/health.go @@ -0,0 +1,36 @@ +package health + +import ( + "context" + "net/http" + + "github.com/Roshick/manifest-maestro/internal/web/header" + "github.com/Roshick/manifest-maestro/internal/web/mimetype" + + "github.com/Roshick/manifest-maestro/internal/web/helper" + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/Roshick/manifest-maestro/pkg/utils/commonutils" + "github.com/go-chi/chi/v5" +) + +func New() *Controller { + return &Controller{} +} + +type Controller struct{} + +func (c *Controller) WireUp(_ context.Context, router chi.Router) { + router.Route("/health", func(router chi.Router) { + router.Get("/readiness", c.Health) + router.Get("/liveness", c.Health) + }) +} + +func (c *Controller) Health(w http.ResponseWriter, r *http.Request) { + response := api.HealthResponse{ + Status: commonutils.Ptr("UP"), + } + r.Context().Err() + w.Header().Set(header.ContentType, mimetype.ApplicationJSON) + helper.WriteJSON(r.Context(), w, response) +} diff --git a/internal/web/controller/metrics/metrics.go b/internal/web/controller/metrics/metrics.go new file mode 100644 index 00000000..4807c273 --- /dev/null +++ b/internal/web/controller/metrics/metrics.go @@ -0,0 +1,18 @@ +package metrics + +import ( + "context" + + "github.com/go-chi/chi/v5" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +func New() *Controller { + return &Controller{} +} + +type Controller struct{} + +func (c *Controller) WireUp(_ context.Context, router chi.Router) { + router.Handle("/metrics", promhttp.Handler()) +} diff --git a/internal/web/controller/swagger/swagger.go b/internal/web/controller/swagger/swagger.go new file mode 100644 index 00000000..316dd830 --- /dev/null +++ b/internal/web/controller/swagger/swagger.go @@ -0,0 +1,129 @@ +package swagger + +import ( + "context" + "fmt" + "net/http" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/Roshick/manifest-maestro/internal/web/header" + "github.com/Roshick/manifest-maestro/internal/web/mimetype" + + aulogging "github.com/StephanHCB/go-autumn-logging" + auwebswaggerui "github.com/StephanHCB/go-autumn-web-swagger-ui" + "github.com/go-chi/chi/v5" +) + +type SpecFile struct { + RelativeFilesystemPath string + FileName string + UriPath string +} + +func New() *Controller { + return &Controller{} +} + +type Controller struct { +} + +func (c *Controller) WireUp(_ context.Context, router chi.Router, additionalSpecFiles ...SpecFile) { + c.AddStaticHttpFilesystemRoute(router, auwebswaggerui.Assets, "/swagger-ui") + openApiSpecFile, err := c.GetFirstMatchingServiceableFile([]string{"api"}, regexp.MustCompile(`openapi[.]yaml`)) + if err != nil { + aulogging.Logger.NoCtx().Error().Print("failed to find OpenAPI specification file. OpenAPI specification will be unavailable.") + return + } + + if err = c.AddStaticFileRoute(router, openApiSpecFile); err != nil { + aulogging.Logger.NoCtx().Error().Printf("failed to read OpenAPI specification file %s. OpenAPI specification will be unavailable.", filepath.Join(openApiSpecFile.RelativeFilesystemPath, openApiSpecFile.FileName)) + return + } + + for _, additionalFile := range additionalSpecFiles { + if err = c.AddStaticFileRoute(router, additionalFile); err != nil { + aulogging.Logger.NoCtx().Error().Printf("failed to read additional OpenAPI specification file %s. OpenAPI specification will be broken.", filepath.Join(additionalFile.RelativeFilesystemPath, additionalFile.FileName)) + } + } + + c.AddRedirect(router, "/v3/api-docs", fmt.Sprintf("/%s", openApiSpecFile.FileName)) +} + +func (c *Controller) AddStaticHttpFilesystemRoute(server chi.Router, fs http.FileSystem, uriPath string) { + strippedFs := http.StripPrefix(uriPath, http.FileServer(fs)) + + if hasNoTrailingSlash(uriPath) { + server.Get(uriPath, http.RedirectHandler(uriPath+"/", 301).ServeHTTP) + uriPath += "/" + } + uriPath += "*" + + server.Get(uriPath, func(w http.ResponseWriter, r *http.Request) { + strippedFs.ServeHTTP(w, r) + }) +} + +func (c *Controller) AddStaticFileRoute(server chi.Router, specFile SpecFile) error { + workDir, _ := os.Getwd() + filePath := filepath.Join(workDir, specFile.RelativeFilesystemPath, specFile.FileName) + + contents, err := os.ReadFile(filePath) + if err != nil { + aulogging.Logger.NoCtx().Info().WithErr(err).Printf("failed to read file %s - skipping: %s", filePath, err.Error()) + return err + } + + if hasNoTrailingSlash(specFile.UriPath) { + specFile.UriPath = specFile.UriPath + "/" + } + + server.Get(specFile.UriPath+specFile.FileName, func(w http.ResponseWriter, r *http.Request) { + // this stops browsers from caching our swagger.json + w.Header().Set(header.CacheControl, "no-gitrepositorycache") + w.Header().Set(header.ContentType, mimetype.ApplicationJSON) + _, _ = w.Write(contents) + }) + + return nil +} + +func (c *Controller) GetFirstMatchingServiceableFile(relativeFilesystemPaths []string, fileMatcher *regexp.Regexp) (SpecFile, error) { + if fileMatcher == nil { + return SpecFile{}, fmt.Errorf("file matcher is nil") + } + + workDir, _ := os.Getwd() + for _, relativeFilesystemPath := range relativeFilesystemPaths { + dirPath := filepath.Join(workDir, relativeFilesystemPath) + + contents, err := os.ReadDir(dirPath) + if err != nil { + aulogging.Logger.NoCtx().Info().WithErr(err).Printf("failed to read directory %s - skipping directory", dirPath) + continue + } + + for _, element := range contents { + if !element.IsDir() && fileMatcher.MatchString(element.Name()) { + return SpecFile{ + RelativeFilesystemPath: relativeFilesystemPath, + FileName: element.Name(), + }, nil + } + } + + } + return SpecFile{}, fmt.Errorf("no file matching %s found in relative paths %s", fileMatcher.String(), strings.Join(relativeFilesystemPaths, ", ")) +} + +func hasNoTrailingSlash(path string) bool { + return len(path) == 0 || path[len(path)-1] != '/' +} + +func (c *Controller) AddRedirect(server chi.Router, source string, target string) { + server.Get(source, func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, target, http.StatusMovedPermanently) + }) +} diff --git a/internal/web/controller/v1/v1.go b/internal/web/controller/v1/v1.go new file mode 100644 index 00000000..c458a99b --- /dev/null +++ b/internal/web/controller/v1/v1.go @@ -0,0 +1,149 @@ +package v1controller + +import ( + "context" + "github.com/Roshick/manifest-maestro/internal/service/helm" + "github.com/Roshick/manifest-maestro/internal/service/kustomize" + "net/http" + "time" + + "github.com/Roshick/manifest-maestro/internal/web/helper" + + "github.com/Roshick/manifest-maestro/pkg/api" + "github.com/go-chi/chi/v5" +) + +type Controller struct { + clock Clock + + helmChartProvider *helm.ChartProvider + helmChartRenderer *helm.ChartRenderer + kustomizationProvider *kustomize.KustomizationProvider + kustomizationRenderer *kustomize.KustomizationRenderer +} + +type Clock interface { + Now() time.Time +} + +func New( + clock Clock, + helmChartProvider *helm.ChartProvider, + helmChartRenderer *helm.ChartRenderer, + kustomizationProvider *kustomize.KustomizationProvider, + kustomizationRenderer *kustomize.KustomizationRenderer, +) *Controller { + return &Controller{ + clock: clock, + helmChartProvider: helmChartProvider, + helmChartRenderer: helmChartRenderer, + kustomizationProvider: kustomizationProvider, + kustomizationRenderer: kustomizationRenderer, + } +} + +func (c *Controller) WireUp(_ context.Context, router chi.Router) { + router.Route("/rest/api/v1", func(router chi.Router) { + router.Route("/helm/actions", func(router chi.Router) { + router.Post("/list-charts", c.helmListCharts) + router.Post("/get-chart-metadata", c.helmGetChartMetadata) + router.Post("/render-chart", c.helmActionsRender) + }) + router.Route("/kustomize/actions", func(router chi.Router) { + router.Post("/render-kustomization", c.kustomizeRenderKustomization) + }) + }) +} + +func (c *Controller) helmListCharts(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + action := api.HelmListChartsAction{} + if err := helper.ParseBody(ctx, r.Body, &action); err != nil { + helper.BadRequestErrorHandler(ctx, w, r, err.Error(), c.clock.Now()) + return + } + + helmCharts, err := c.helmChartProvider.ListHelmCharts(ctx, action.Reference) + if err != nil { + helper.HandleError(ctx, w, r, err, c.clock.Now()) + return + } + + helper.Success(ctx, w, r, api.HelmListChartsActionResponse{ + Items: helmCharts, + }) +} + +func (c *Controller) helmGetChartMetadata(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + action := api.HelmGetChartMetadataAction{} + if err := helper.ParseBody(ctx, r.Body, &action); err != nil { + helper.BadRequestErrorHandler(ctx, w, r, err.Error(), c.clock.Now()) + return + } + + helmChart, err := c.helmChartProvider.GetHelmChart(ctx, action.Reference) + if err != nil { + helper.HandleError(ctx, w, r, err, c.clock.Now()) + return + } + + helper.Success(ctx, w, r, api.HelmGetChartMetadataActionResponse{ + DefaultValues: helmChart.DefaultValues(), + }) +} + +func (c *Controller) helmActionsRender(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + action := api.HelmRenderChartAction{} + if err := helper.ParseBody(ctx, r.Body, &action); err != nil { + helper.BadRequestErrorHandler(ctx, w, r, err.Error(), c.clock.Now()) + return + } + + helmChart, err := c.helmChartProvider.GetHelmChart(ctx, action.Reference) + if err != nil { + helper.HandleError(ctx, w, r, err, c.clock.Now()) + return + } + + manifests, metadata, err := c.helmChartRenderer.Render(ctx, helmChart, action.Parameters) + if err != nil { + helper.HandleError(ctx, w, r, err, c.clock.Now()) + return + } + + helper.Success(ctx, w, r, api.HelmRenderChartActionResponse{ + Manifests: manifests, + Metadata: metadata, + }) +} + +func (c *Controller) kustomizeRenderKustomization(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + action := api.KustomizeRenderKustomizationAction{} + if err := helper.ParseBody(ctx, r.Body, &action); err != nil { + helper.BadRequestErrorHandler(ctx, w, r, err.Error(), c.clock.Now()) + return + } + + kustomization, err := c.kustomizationProvider.GetKustomization(ctx, action.Reference) + if err != nil { + helper.HandleError(ctx, w, r, err, c.clock.Now()) + return + } + + manifests, err := c.kustomizationRenderer.Render(ctx, kustomization, action.Parameters) + if err != nil { + helper.HandleError(ctx, w, r, err, c.clock.Now()) + return + } + + helper.Success(ctx, w, r, api.KustomizeRenderKustomizationActionResponse{ + Manifests: manifests, + }) +} diff --git a/internal/web/header/header.go b/internal/web/header/header.go new file mode 100644 index 00000000..ba699301 --- /dev/null +++ b/internal/web/header/header.go @@ -0,0 +1,14 @@ +package header + +const ( + Accept = "Accept" + AccessControlAllowOrigin = "Access-Control-Allow-Origin" + AccessControlAllowMethods = "Access-Control-Allow-Methods" + AccessControlAllowHeaders = "Access-Control-Allow-Headers" + AccessControlAllowCredentials = "Access-Control-Allow-Credentials" + AccessControlExposeHeaders = "Access-Control-Expose-Headers" + CacheControl = "Cache-Control" + ContentType = "Content-Type" + ContentSecurityPolicy = "Content-Security-Policy" + Location = "Location" +) diff --git a/internal/web/helper/helper.go b/internal/web/helper/helper.go new file mode 100644 index 00000000..4baaf1eb --- /dev/null +++ b/internal/web/helper/helper.go @@ -0,0 +1,134 @@ +package helper + +import ( + "context" + "encoding/json" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/Roshick/manifest-maestro/internal/web/header" + "github.com/Roshick/manifest-maestro/internal/web/mimetype" + + "github.com/Roshick/manifest-maestro/pkg/api" + aulogging "github.com/StephanHCB/go-autumn-logging" + "github.com/go-chi/chi/v5" + "gopkg.in/yaml.v3" +) + +// ToDo rework + +// --- success handlers --- + +func Success(ctx context.Context, w http.ResponseWriter, r *http.Request, response any) { + acceptEncodingHeader := r.Header.Get(header.Accept) + if strings.Contains(acceptEncodingHeader, mimetype.ApplicationYAML) { + w.Header().Set(header.ContentType, mimetype.ApplicationYAML) + WriteYAML(ctx, w, response) + } else { + w.Header().Set(header.ContentType, mimetype.ApplicationJSON) + WriteJSON(ctx, w, response) + } +} + +func NoContent(_ context.Context, w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusNoContent) +} + +// --- error handlers --- + +func HandleError(ctx context.Context, w http.ResponseWriter, r *http.Request, err error, timeStamp time.Time) { + UnexpectedErrorHandler(ctx, w, r, err.Error(), timeStamp) +} + +func BadRequestErrorHandler( + ctx context.Context, + w http.ResponseWriter, + r *http.Request, + logMessage string, + timeStamp time.Time, +) { + aulogging.Logger.Ctx(ctx).Info().Printf("bad request: %s", logMessage) + errorHandler(ctx, w, r, "input.invalid", http.StatusBadRequest, logMessage, timeStamp) +} + +func UnexpectedErrorHandler( + ctx context.Context, + w http.ResponseWriter, + r *http.Request, + logMessage string, + timeStamp time.Time, +) { + aulogging.Logger.Ctx(ctx).Error().Printf("unexpected: %s", logMessage) + errorHandler(ctx, w, r, "unknown", http.StatusInternalServerError, logMessage, timeStamp) +} + +func errorHandler( + ctx context.Context, + w http.ResponseWriter, + _ *http.Request, + msg string, + status int, + details string, + timestamp time.Time, +) { + response := &api.ErrorResponse{} + w.Header().Set(header.ContentType, mimetype.ApplicationJSON) + w.WriteHeader(status) + WriteJSON(ctx, w, response) +} + +// --- helpers + +func StringPathParam(r *http.Request, key string) (string, error) { + value := chi.URLParam(r, key) + return url.PathUnescape(value) +} + +func StringQueryParam(r *http.Request, key string, defaultValue string) (string, error) { + query := r.URL.Query() + value := query.Get(key) + if value == "" { + return defaultValue, nil + } + return url.QueryUnescape(value) +} + +func BooleanQueryParam(r *http.Request, key string, defaultValue bool) (bool, error) { + query := r.URL.Query() + value := query.Get(key) + if value == "" { + return defaultValue, nil + } + return strconv.ParseBool(query.Get(key)) +} + +func WriteJSON(ctx context.Context, w http.ResponseWriter, v any) { + encoder := json.NewEncoder(w) + encoder.SetEscapeHTML(false) + err := encoder.Encode(v) + if err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("error while encoding json response: %v", err) + // can't change status anymore, in the middle of the response now + } +} + +func WriteYAML(ctx context.Context, w http.ResponseWriter, v any) { + encoder := yaml.NewEncoder(w) + err := encoder.Encode(v) + if err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("error while encoding yaml response: %v", err) + // can't change status anymore, in the middle of the response now + } +} + +func ParseBody(_ context.Context, body io.ReadCloser, resource any) error { + jsonBytes, err := io.ReadAll(body) + if err != nil { + return err + } + return json.Unmarshal(jsonBytes, resource) +} diff --git a/internal/web/middleware/middleware.go b/internal/web/middleware/middleware.go new file mode 100755 index 00000000..c7e078f2 --- /dev/null +++ b/internal/web/middleware/middleware.go @@ -0,0 +1,317 @@ +package middleware + +import ( + "context" + "fmt" + "net/http" + "regexp" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/Roshick/manifest-maestro/internal/web/header" + "github.com/Roshick/manifest-maestro/pkg/utils/commonutils" + "github.com/go-chi/chi/v5" + "github.com/prometheus/client_golang/prometheus" + + "github.com/Roshick/go-autumn-slog/pkg/logging" + aulogging "github.com/StephanHCB/go-autumn-logging" + "github.com/go-chi/chi/v5/middleware" +) + +const ( + LogFieldRequestMethod = "request-method" + LogFieldRequestID = "request-id" + LogFieldResponseStatus = "response-status" + LogFieldURLPath = "url-path" + LogFieldUserAgent = "user-agent" + LogFieldEventDuration = "event-duration" + LogFieldLogger = "logger" + LogFieldStackTrace = "stack-trace" +) + +// AddLoggerToContext // + +func AddLoggerToContext(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + if slogging, ok := aulogging.Logger.(*logging.Logging); ok { + ctx = logging.ContextWithLogger(ctx, slogging.Logger()) + } + + next.ServeHTTP(w, r.WithContext(ctx)) + } + return http.HandlerFunc(fn) +} + +// AddRequestIDToContext // + +type AddRequestIDToContextOptions struct { + RequestIDHeader string + RequestIDFunc func() string +} + +type requestIDContextKey struct{} + +func RequestIDFromContext(ctx context.Context) *string { + if value := ctx.Value(requestIDContextKey{}); value != nil { + return commonutils.Ptr(value.(string)) + } + return nil +} + +func ContextWithRequestID(ctx context.Context, requestID string) context.Context { + return context.WithValue(ctx, requestIDContextKey{}, requestID) +} + +func CreateAddRequestIDToContext(options AddRequestIDToContextOptions) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + if requestID := r.Header.Get(options.RequestIDHeader); requestID == "" { + requestID = options.RequestIDFunc() + ctx = ContextWithRequestID(ctx, requestID) + } + + next.ServeHTTP(w, r.WithContext(ctx)) + } + return http.HandlerFunc(fn) + } +} + +// AddRequestIDToContextLogger // + +func AddRequestIDToContextLogger(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + if logger := logging.FromContext(ctx); logger != nil { + requestID := RequestIDFromContext(ctx) + if commonutils.DefaultIfEmpty(requestID, "") != "" { + logger = logger.With(LogFieldRequestID, *requestID) + } + ctx = logging.ContextWithLogger(ctx, logger) + } + + next.ServeHTTP(w, r.WithContext(ctx)) + } + return http.HandlerFunc(fn) +} + +// AddRequestIDToResponseHeader // + +type AddRequestIDToResponseHeaderOptions struct { + RequestIDHeader string +} + +func CreateAddRequestIDToResponseHeader(options AddRequestIDToResponseHeaderOptions) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + if requestID := RequestIDFromContext(ctx); commonutils.DefaultIfEmpty(requestID, "") != "" { + w.Header().Set(options.RequestIDHeader, *requestID) + } + + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + } +} + +// AddRequestResponseContextLogging // + +type AddRequestResponseContextLoggingOptions struct { + ExcludeLogging []string +} + +func CreateAddRequestResponseContextLogging(options AddRequestResponseContextLoggingOptions) func(next http.Handler) http.Handler { + excludeRegexes := make([]*regexp.Regexp, 0) + for _, pattern := range options.ExcludeLogging { + fullMatchPattern := "^" + pattern + "$" + re, err := regexp.Compile(fullMatchPattern) + if err != nil { + aulogging.Logger.NoCtx().Error().WithErr(err).Printf("failed to compile exclude logging pattern '%s', skipping pattern", fullMatchPattern) + } else { + excludeRegexes = append(excludeRegexes, re) + } + } + + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) + t1 := time.Now() + defer func() { + ctx := r.Context() + + requestInfo := fmt.Sprintf("%s %s %d", r.Method, r.URL.Path, ww.Status()) + for _, re := range excludeRegexes { + if re.MatchString(requestInfo) { + return + } + } + + if logger := logging.FromContext(ctx); logger != nil { + logger = logger.With( + LogFieldRequestMethod, r.Method, + LogFieldResponseStatus, ww.Status(), + LogFieldURLPath, r.URL.Path, + LogFieldUserAgent, r.UserAgent(), + LogFieldLogger, "request.incoming", + LogFieldEventDuration, time.Since(t1).Microseconds(), + ) + subCtx := logging.ContextWithLogger(ctx, logger) + if ww.Status() >= http.StatusInternalServerError { + aulogging.Logger.Ctx(subCtx).Error().Print("request") + } else { + aulogging.Logger.Ctx(subCtx).Info().Print("request") + } + } + }() + + next.ServeHTTP(ww, r) + } + return http.HandlerFunc(fn) + } +} + +// AddRequestTimeout // + +type AddRequestTimeoutOptions struct { + RequestTimeoutInSeconds int +} + +func CreateAddRequestTimeout(options AddRequestTimeoutOptions) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + ctx, cancel := context.WithTimeout(ctx, time.Duration(options.RequestTimeoutInSeconds)*time.Second) + defer cancel() + + next.ServeHTTP(w, r.WithContext(ctx)) + } + return http.HandlerFunc(fn) + } +} + +// AddContextCancelLogging // + +type LogContextCancellationOptions struct { + Description string +} + +func CreateLogContextCancellation(options LogContextCancellationOptions) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + next.ServeHTTP(w, r) + + cause := context.Cause(ctx) + if cause != nil { + aulogging.Logger.NoCtx().Info().WithErr(cause).Printf("context '%s' is cancelled", options.Description) + } + } + return http.HandlerFunc(fn) + } +} + +// RecoverPanic // + +func RecoverPanic(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + defer func() { + ctx := r.Context() + rvr := recover() + if rvr != nil && rvr != http.ErrAbortHandler { + if logger := logging.FromContext(ctx); logger != nil { + subCtx := logging.ContextWithLogger(ctx, logger.With(LogFieldStackTrace, debug.Stack())) + aulogging.Logger.Ctx(subCtx).Error().Print("recovered from panic") + } + w.WriteHeader(http.StatusInternalServerError) + } + }() + + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) +} + +// HandleCORS // + +type HandleCORSOptions struct { + AllowOrigin string + AdditionalAllowHeaders []string + AdditionalExposeHeaders []string +} + +func CreateHandleCORS(options HandleCORSOptions) func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + w.Header().Set(header.AccessControlAllowOrigin, options.AllowOrigin) + + w.Header().Set(header.AccessControlAllowMethods, strings.Join([]string{ + http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + }, ", ")) + + w.Header().Set(header.AccessControlAllowHeaders, strings.Join(append([]string{ + header.Accept, + header.ContentType, + }, options.AdditionalAllowHeaders...), ", ")) + + w.Header().Set(header.AccessControlAllowCredentials, "true") + + w.Header().Set(header.AccessControlExposeHeaders, strings.Join(append([]string{ + header.CacheControl, + header.ContentSecurityPolicy, + header.ContentType, + header.Location, + }, options.AdditionalExposeHeaders...), ", ")) + + if r.Method == http.MethodOptions { + w.WriteHeader(http.StatusOK) + return + } + + next.ServeHTTP(w, r) + } + return http.HandlerFunc(fn) + } +} + +// RecordRequestMetrics // + +func CreateRecordRequestMetrics() func(next http.Handler) http.Handler { + requests := prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Name: "http_server_requests_seconds", + Help: "How long it took to process requests, partitioned by status code, method and HTTP path (grouped by patterns).", + }, + []string{"method", "status", "uri"}, + ) + prometheus.MustRegister(requests) + + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + start := time.Now() + ww := middleware.NewWrapResponseWriter(w, r.ProtoMajor) + next.ServeHTTP(ww, r) + + routeCtx := chi.RouteContext(r.Context()) + routePattern := strings.Join(routeCtx.RoutePatterns, "") + routePattern = strings.Replace(routePattern, "/*/", "/", -1) + + requests.WithLabelValues(r.Method, strconv.Itoa(ww.Status()), routePattern).Observe(float64(time.Since(start).Microseconds()) / 1000000) + } + return http.HandlerFunc(fn) + } +} diff --git a/internal/web/mimetype/mimetype.go b/internal/web/mimetype/mimetype.go new file mode 100644 index 00000000..1ea6cd4f --- /dev/null +++ b/internal/web/mimetype/mimetype.go @@ -0,0 +1,6 @@ +package mimetype + +const ( + ApplicationJSON = "application/json" + ApplicationYAML = "application/x-yaml" +) diff --git a/internal/web/primary.go b/internal/web/primary.go new file mode 100755 index 00000000..e578cc2b --- /dev/null +++ b/internal/web/primary.go @@ -0,0 +1,26 @@ +package web + +import ( + "context" + "errors" + "fmt" + "net/http" + + aulogging "github.com/StephanHCB/go-autumn-logging" +) + +func (s *Server) CreatePrimaryServer(ctx context.Context) *http.Server { + address := fmt.Sprintf("%s:%d", s.config.ServerAddress(), s.config.ServerPrimaryPort()) + aulogging.Logger.Ctx(ctx).Info().Printf("creating primary http server on %s", address) + return s.NewServer(ctx, address, s.Router) +} + +func (s *Server) StartPrimaryServer(ctx context.Context, srv *http.Server) error { + aulogging.Logger.Ctx(ctx).Info().Print("starting primary http server") + err := srv.ListenAndServe() + if err != nil && !errors.Is(err, http.ErrServerClosed) { + return fmt.Errorf("failed to start primary http server: %w", err) + } + aulogging.Logger.Ctx(ctx).Info().Print("primary http server has shut down") + return nil +} diff --git a/internal/web/server.go b/internal/web/server.go new file mode 100755 index 00000000..f6ee403c --- /dev/null +++ b/internal/web/server.go @@ -0,0 +1,202 @@ +package web + +import ( + "context" + "net" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + healthcontroller "github.com/Roshick/manifest-maestro/internal/web/controller/health" + metricscontroller "github.com/Roshick/manifest-maestro/internal/web/controller/metrics" + swaggercontroller "github.com/Roshick/manifest-maestro/internal/web/controller/swagger" + v1controller "github.com/Roshick/manifest-maestro/internal/web/controller/v1" + "github.com/google/uuid" + + "github.com/Roshick/manifest-maestro/internal/config" + "github.com/Roshick/manifest-maestro/internal/web/middleware" + aulogging "github.com/StephanHCB/go-autumn-logging" + "github.com/go-chi/chi/v5" +) + +type Server struct { + config *config.ApplicationConfig + + healthController *healthcontroller.Controller + swaggerController *swaggercontroller.Controller + metricsController *metricscontroller.Controller + v1Controller *v1controller.Controller + + Router chi.Router + + RequestTimeoutInSeconds int + ServerReadTimeoutInSeconds int + ServerWriteTimeoutInSeconds int + ServerIdleTimeoutInSeconds int + GracePeriodInSeconds int +} + +func NewServer( + ctx context.Context, + config *config.ApplicationConfig, + healthController *healthcontroller.Controller, + swaggerController *swaggercontroller.Controller, + metricsController *metricscontroller.Controller, + v1Controller *v1controller.Controller, +) (*Server, error) { + server := &Server{ + config: config, + + healthController: healthController, + swaggerController: swaggerController, + metricsController: metricsController, + v1Controller: v1Controller, + + RequestTimeoutInSeconds: 60, + ServerWriteTimeoutInSeconds: 60, + ServerIdleTimeoutInSeconds: 60, + ServerReadTimeoutInSeconds: 60, + GracePeriodInSeconds: 30, + } + + if err := server.WireUp(ctx); err != nil { + return nil, err + } + + return server, nil +} + +func (s *Server) WireUp(ctx context.Context) error { + if s.Router == nil { + aulogging.Logger.Ctx(ctx).Info().Print("creating router and setting up filter chain") + s.Router = chi.NewRouter() + + if err := s.setupMiddlewareStack(ctx); err != nil { + return err + } + + } + + s.healthController.WireUp(ctx, s.Router) + s.swaggerController.WireUp(ctx, s.Router) + s.metricsController.WireUp(ctx, s.Router) + s.v1Controller.WireUp(ctx, s.Router) + + return nil +} + +func (s *Server) setupMiddlewareStack(_ context.Context) error { + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "Top", + })) + + s.Router.Use(middleware.AddLoggerToContext) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "AddLoggerToContext", + })) + + s.Router.Use(middleware.CreateAddRequestIDToContext(middleware.AddRequestIDToContextOptions{ + RequestIDHeader: "X-Request-ID", + RequestIDFunc: uuid.NewString, + })) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "AddRequestIDToContext", + })) + + s.Router.Use(middleware.CreateAddRequestIDToResponseHeader(middleware.AddRequestIDToResponseHeaderOptions{ + RequestIDHeader: "X-Request-ID", + })) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "AddRequestIDToResponseHeader", + })) + + s.Router.Use(middleware.AddRequestIDToContextLogger) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "AddRequestIDToContextLogger", + })) + + s.Router.Use(middleware.CreateAddRequestResponseContextLogging(middleware.AddRequestResponseContextLoggingOptions{ + ExcludeLogging: []string{ + "GET /health/ready 200", + "GET /health/live 200", + "GET /metrics 200", + }, + })) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "AddRequestResponseContextLogging", + })) + + s.Router.Use(middleware.RecoverPanic) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "RecoverPanic", + })) + + s.Router.Use(middleware.CreateHandleCORS(middleware.HandleCORSOptions{ + AllowOrigin: "*", + AdditionalAllowHeaders: []string{"X-Request-ID"}, + AdditionalExposeHeaders: []string{"X-Request-ID"}, + })) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "HandleCORS", + })) + + s.Router.Use(middleware.CreateRecordRequestMetrics()) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "RecordRequestMetrics", + })) + + s.Router.Use(middleware.CreateAddRequestTimeout(middleware.AddRequestTimeoutOptions{ + RequestTimeoutInSeconds: s.RequestTimeoutInSeconds, + })) + s.Router.Use(middleware.CreateLogContextCancellation(middleware.LogContextCancellationOptions{ + Description: "AddRequestTimeout", + })) + + return nil +} + +func (s *Server) NewServer(ctx context.Context, address string, router http.Handler) *http.Server { + return &http.Server{ + Addr: address, + Handler: router, + ReadTimeout: 10 * time.Second, + WriteTimeout: 60 * time.Second, + IdleTimeout: 60 * time.Second, + BaseContext: func(_ net.Listener) context.Context { + return ctx + }, + } +} + +func (s *Server) Run(ctx context.Context) error { + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM) + + srvPrimary := s.CreatePrimaryServer(ctx) + + go func() { + <-sig // wait for signal notification + + tCtx, tCancel := context.WithTimeout(context.Background(), time.Duration(s.GracePeriodInSeconds)*time.Second) + defer tCancel() + + aulogging.Logger.NoCtx().Debug().Print("stopping services now") + + if err := srvPrimary.Shutdown(tCtx); err != nil { + aulogging.Logger.NoCtx().Error().WithErr(err). + Printf("failed to shut down primary http web gracefully within %d seconds: %s", s.GracePeriodInSeconds, err.Error()) + // this is not perfect, but we need to terminate the entire process because we've trapped sigterm + os.Exit(config.ExitCodeDirtyShutdown) + } + }() + + if err := s.StartPrimaryServer(ctx, srvPrimary); err != nil { + aulogging.Logger.Ctx(ctx).Fatal().WithErr(err).Print("failed to start primary server") + return err + } + + aulogging.Logger.Ctx(ctx).Info().Print("web layer torn down successfully") + return nil +} diff --git a/internal/wiring/application.go b/internal/wiring/application.go new file mode 100644 index 00000000..c3cebdb5 --- /dev/null +++ b/internal/wiring/application.go @@ -0,0 +1,407 @@ +package wiring + +import ( + "context" + "fmt" + aucache "github.com/Roshick/go-autumn-synchronisation/pkg/cache" + "github.com/Roshick/manifest-maestro/internal/service/cache" + "log/slog" + "os" + "time" + + healthcontroller "github.com/Roshick/manifest-maestro/internal/web/controller/health" + metricscontroller "github.com/Roshick/manifest-maestro/internal/web/controller/metrics" + swaggercontroller "github.com/Roshick/manifest-maestro/internal/web/controller/swagger" + v1controller "github.com/Roshick/manifest-maestro/internal/web/controller/v1" + "github.com/go-git/go-git/v5/plumbing" + + "github.com/Roshick/go-autumn-configloader/pkg/configloader" + "github.com/Roshick/go-autumn-slog/pkg/logging" + "github.com/Roshick/go-autumn-vault/pkg/vault" + "github.com/Roshick/manifest-maestro/internal/repository/clock" + augit "github.com/Roshick/manifest-maestro/internal/repository/git" + "github.com/Roshick/manifest-maestro/internal/repository/helmremote" + "github.com/Roshick/manifest-maestro/internal/service/helm" + "github.com/Roshick/manifest-maestro/internal/service/kustomize" + aulogging "github.com/StephanHCB/go-autumn-logging" + "github.com/go-git/go-git/v5" + + "github.com/Roshick/manifest-maestro/internal/config" + "github.com/Roshick/manifest-maestro/internal/web" +) + +var ( + LocalConfigFilename = "local-config.yaml" +) + +type Clock interface { + Now() time.Time +} + +type Git interface { + FetchReferences(context.Context, string) ([]*plumbing.Reference, error) + + CloneCommit(context.Context, string, string) (*git.Repository, error) +} + +type HelmRemote interface { + GetIndex(context.Context, string) ([]byte, error) + + GetChart(context.Context, string) ([]byte, error) +} + +type Application struct { + // bootstrap + ConfigLoader *configloader.ConfigLoader + Logger *slog.Logger + Config *config.Config + VaultClient vault.Client + Vault *vault.Vault + + // repositories (outgoing connectors) + Clock Clock + Git Git + HelmRemote HelmRemote + + // services (business logic) + GitRepositoryCache *cache.GitRepositoryCache + HelmIndexCache *cache.HelmIndexCache + HelmChartCache *cache.HelmChartCache + HelmChartProvider *helm.ChartProvider + HelmChartRenderer *helm.ChartRenderer + KustomizationProvider *kustomize.KustomizationProvider + KustomizationRenderer *kustomize.KustomizationRenderer + + // web stack + // controllers (incoming connectors) + HealthController *healthcontroller.Controller + SwaggerController *swaggercontroller.Controller + MetricsController *metricscontroller.Controller + V1Controller *v1controller.Controller + + // server + Server *web.Server +} + +func NewApplication() *Application { + return &Application{} +} + +func (a *Application) Create(ctx context.Context) error { + if err := a.createConfigLoader(ctx); err != nil { + return fmt.Errorf("failed to set up configuration loader: %w", err) + } + if err := a.createConfig(ctx); err != nil { + return fmt.Errorf("failed to set up configuration: %w", err) + } + if err := a.loadBootstrapConfig(ctx); err != nil { + return fmt.Errorf("failed to load bootstrap config: %w", err) + } + + if err := a.createLogging(ctx); err != nil { + return fmt.Errorf("failed to set up logging: %w", err) + } + + if err := a.createVault(ctx); err != nil { + return fmt.Errorf("failed to set up vault: %w", err) + } + if err := a.loadApplicationConfig(ctx); err != nil { + return fmt.Errorf("failed to load application config: %w", err) + } + + // repositories (outgoing connectors) + if err := a.createClock(ctx); err != nil { + return fmt.Errorf("failed to set up clock: %w", err) + } + if err := a.createGit(ctx); err != nil { + return fmt.Errorf("failed to set up git: %w", err) + } + if err := a.createHelmRemote(ctx); err != nil { + return fmt.Errorf("failed to set up helm-remote: %w", err) + } + + // services (business logic) + if err := a.createGitRepositoryCache(ctx); err != nil { + return fmt.Errorf("failed to set up git repository cache: %w", err) + } + if err := a.createHelmIndexCache(ctx); err != nil { + return fmt.Errorf("failed to set up helm index cache: %w", err) + } + if err := a.createHelmChartCache(ctx); err != nil { + return fmt.Errorf("failed to set up helm chart cache: %w", err) + } + if err := a.createHelmChartProvider(ctx); err != nil { + return fmt.Errorf("failed to set up helm chart provider: %w", err) + } + if err := a.createHelmChartRenderer(ctx); err != nil { + return fmt.Errorf("failed to set up helm chart renderer: %w", err) + } + if err := a.createKustomizationProvider(ctx); err != nil { + return fmt.Errorf("failed to set up kustomization provider: %w", err) + } + if err := a.createKustomizationRenderer(ctx); err != nil { + return fmt.Errorf("failed to set up kustomization renderer: %w", err) + } + + // web stack + if err := a.createHealthController(ctx); err != nil { + return fmt.Errorf("failed to set up health controller: %w", err) + } + if err := a.createSwaggerController(ctx); err != nil { + return fmt.Errorf("failed to set up swagger controller: %w", err) + } + if err := a.createMetricsController(ctx); err != nil { + return fmt.Errorf("failed to set up metrics controller: %w", err) + } + if err := a.createV1Controller(ctx); err != nil { + return fmt.Errorf("failed to set up v1 controller: %w", err) + } + if err := a.createServer(ctx); err != nil { + return fmt.Errorf("failed to set up server: %w", err) + } + return nil +} + +func (a *Application) Teardown(_ context.Context, cancel context.CancelFunc) { + cancel() +} + +func (a *Application) Run() int { + ctx, cancel := context.WithCancel(context.Background()) + defer func() { + a.Teardown(ctx, cancel) + }() + + if err := a.Create(ctx); err != nil { + aulogging.Logger.Ctx(ctx).Error().WithErr(err).Printf("failed to create application") + return config.ExitCodeCreateFailed + } + + if err := a.Server.Run(ctx); err != nil { + aulogging.Logger.Ctx(ctx).Error().WithErr(err).Printf("failed to run application") + return config.ExitCodeRunFailed + } + + return config.ExitCodeSuccess +} + +func (a *Application) createConfigLoader(_ context.Context) error { + if a.ConfigLoader == nil { + a.ConfigLoader = configloader.New() + } + return nil +} + +func (a *Application) createConfig(_ context.Context) error { + if a.Config == nil { + a.Config = config.New() + } + return nil +} + +func (a *Application) loadBootstrapConfig(_ context.Context) error { + providers := defaultProviders(LocalConfigFilename) + + return a.ConfigLoader.LoadConfig(a.Config.Bootstrap(), providers...) +} + +func (a *Application) createLogging(_ context.Context) error { + if a.Logger == nil { + providers := defaultProviders(LocalConfigFilename) + + loggingConfig := logging.NewConfig() + if err := a.ConfigLoader.LoadConfig(loggingConfig, providers...); err != nil { + return err + } + + if a.Config.Bootstrap().LogType() == config.LogStyleJSON { + a.Logger = slog.New(slog.NewJSONHandler(os.Stderr, loggingConfig.HandlerOptions())) + } else { + a.Logger = slog.New(slog.NewTextHandler(os.Stderr, loggingConfig.HandlerOptions())) + } + } + + slog.SetDefault(a.Logger) + aulogging.Logger = logging.New() + return nil +} + +func (a *Application) createVault(ctx context.Context) error { + if a.Config.Bootstrap().VaultEnabled() && a.Vault == nil { + providers := defaultProviders(LocalConfigFilename) + + if err := a.ConfigLoader.LoadConfig(a.Config.Vault(), providers...); err != nil { + return err + } + if a.VaultClient == nil { + if vaultClient, err := vault.NewClient(ctx, a.Config.Vault()); err != nil { + return err + } else { + a.VaultClient = vaultClient + } + } + a.Vault = vault.New(a.Config.Vault(), a.VaultClient) + } + return nil +} + +func (a *Application) loadApplicationConfig(_ context.Context) error { + providers := defaultProviders(LocalConfigFilename) + if a.Config.Bootstrap().VaultEnabled() && a.Vault != nil { + providers = append(providers, a.Vault.ValuesProvider()) + } + + if err := a.ConfigLoader.LoadConfig(a.Config.Application(), providers...); err != nil { + return err + } + + slog.SetDefault(slog.Default().With("application", a.Config.Application().ApplicationName())) + + return nil +} + +func (a *Application) createClock(_ context.Context) error { + if a.Clock == nil { + a.Clock = clock.New() + } + return nil +} + +func (a *Application) createGit(_ context.Context) error { + if a.Git == nil { + if iGit, err := augit.New(a.Config.Application()); err != nil { + return err + } else { + a.Git = iGit + } + } + return nil +} + +func (a *Application) createHelmRemote(_ context.Context) error { + if a.HelmRemote == nil { + a.HelmRemote = helmremote.New() + } + return nil +} + +func (a *Application) createGitRepositoryCache(ctx context.Context) error { + if a.GitRepositoryCache == nil { + byteSliceCache, err := a.createByteSliceCache(ctx, "git-repository") + if err != nil { + return err + } + a.GitRepositoryCache = cache.NewGitRepositoryCache(a.Git, byteSliceCache) + } + return nil +} + +func (a *Application) createHelmIndexCache(ctx context.Context) error { + if a.HelmIndexCache == nil { + byteSliceCache, err := a.createByteSliceCache(ctx, "helm-index") + if err != nil { + return err + } + a.HelmIndexCache = cache.NewHelmIndexCache(a.HelmRemote, byteSliceCache) + } + return nil +} + +func (a *Application) createHelmChartCache(ctx context.Context) error { + if a.HelmChartCache == nil { + byteSliceCache, err := a.createByteSliceCache(ctx, "helm-chart") + if err != nil { + return err + } + a.HelmChartCache = cache.NewHelmChartCache(a.HelmRemote, a.HelmIndexCache, byteSliceCache) + } + return nil +} + +func (a *Application) createHelmChartProvider(_ context.Context) error { + if a.HelmChartProvider == nil { + a.HelmChartProvider = helm.NewChartProvider(a.HelmChartCache, a.GitRepositoryCache) + } + return nil +} + +func (a *Application) createHelmChartRenderer(_ context.Context) error { + if a.HelmChartRenderer == nil { + apiVersions := a.Config.Application().HelmDefaultKubernetesAPIVersions() + a.HelmChartRenderer = helm.NewChartRenderer(apiVersions) + } + return nil +} + +func (a *Application) createKustomizationProvider(_ context.Context) error { + if a.KustomizationProvider == nil { + a.KustomizationProvider = kustomize.NewKustomizationProvider(a.GitRepositoryCache) + } + return nil +} + +func (a *Application) createKustomizationRenderer(_ context.Context) error { + if a.KustomizationRenderer == nil { + a.KustomizationRenderer = kustomize.NewKustomizationRenderer() + } + return nil +} + +func (a *Application) createHealthController(_ context.Context) error { + if a.HealthController == nil { + a.HealthController = healthcontroller.New() + } + return nil +} + +func (a *Application) createSwaggerController(_ context.Context) error { + if a.SwaggerController == nil { + a.SwaggerController = swaggercontroller.New() + } + return nil +} + +func (a *Application) createMetricsController(_ context.Context) error { + if a.MetricsController == nil { + a.MetricsController = metricscontroller.New() + } + return nil +} + +func (a *Application) createV1Controller(_ context.Context) error { + if a.V1Controller == nil { + a.V1Controller = v1controller.New(a.Clock, a.HelmChartProvider, a.HelmChartRenderer, a.KustomizationProvider, a.KustomizationRenderer) + } + return nil +} + +func (a *Application) createServer(ctx context.Context) error { + if a.Server == nil { + server, err := web.NewServer(ctx, a.Config.Application(), a.HealthController, a.SwaggerController, a.MetricsController, a.V1Controller) + if err != nil { + return err + } + a.Server = server + } + return nil +} + +func (a *Application) createByteSliceCache(_ context.Context, cacheKey string) (aucache.Cache[[]byte], error) { + synchMethod := a.Config.Application().SynchronizationMethod() + switch synchMethod { + case config.SynchronizationMethodRedis: + redisURL := a.Config.Application().SynchronizationRedisURL() + redisPassword := a.Config.Application().SynchronizationRedisPassword() + return aucache.NewRedisCache[[]byte](redisURL, redisPassword, cacheKey) + default: + return aucache.NewMemoryCache[[]byte](), nil + } +} + +func defaultProviders(configPath string) []configloader.Provider { + return []configloader.Provider{ + configloader.CreateDefaultValuesProvider(), + configloader.CreateYAMLConfigFileProvider(configPath), + configloader.CreateEnvironmentVariablesProvider(), + } +} diff --git a/local-config.template.yaml b/local-config.template.yaml new file mode 100644 index 00000000..3213eeb3 --- /dev/null +++ b/local-config.template.yaml @@ -0,0 +1,10 @@ +APPLICATION_NAME: manifest-maestro + +LOG_STYLE: PLAIN + +VAULT_ENABLED: false + +LOG_ATTRIBUTE_KEY_MAPPINGS: >- + { + "service-name": "service.name" + } \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 00000000..2ec07ff8 --- /dev/null +++ b/main.go @@ -0,0 +1,10 @@ +package main + +import ( + "github.com/Roshick/manifest-maestro/internal/wiring" + "os" +) + +func main() { + os.Exit(wiring.NewApplication().Run()) +} diff --git a/openapitools.json b/openapitools.json new file mode 100644 index 00000000..7f73ae2c --- /dev/null +++ b/openapitools.json @@ -0,0 +1,18 @@ +{ + "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", + "spaces": 2, + "generator-cli": { + "version": "7.10.0", + "repository": { + "downloadUrl": "https://github.com/Interhyp/openapi-generator/raw/new_generator_rebased/bin/openapi-generator-cli-${versionName}_INTERHYP.jar" + }, + "generators": { + "api": { + "generatorName": "go", + "inputSpec": "./api/openapi.yaml", + "output": "./pkg/api", + "packageName": "api" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..d1b959de --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "manifest-maestro", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "api": "openapi-generator-cli generate" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@9.6.0+sha512.38dc6fba8dba35b39340b9700112c2fe1e12f10b17134715a4aa98ccf7bb035e76fd981cf0bb384dfa98f8d6af5481c2bef2f4266a24bfa20c34eb7147ce0b5e", + "devDependencies": { + "@openapitools/openapi-generator-cli": "^2.13.4" + } +} diff --git a/pkg/api/.gitignore b/pkg/api/.gitignore new file mode 100644 index 00000000..daf913b1 --- /dev/null +++ b/pkg/api/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/pkg/api/.openapi-generator-ignore b/pkg/api/.openapi-generator-ignore new file mode 100644 index 00000000..681afcd2 --- /dev/null +++ b/pkg/api/.openapi-generator-ignore @@ -0,0 +1,32 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +# As an example, the C# client generator defines ApiClient.cs. +# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line: +#ApiClient.cs + +# You can match any string of characters against a directory, file or extension with a single asterisk (*): +#foo/*/qux +# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux + +# You can recursively match patterns against a directory, file or extension with a double asterisk (**): +#foo/**/qux +# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux + +# You can also negate patterns with an exclamation (!). +# For example, you can ignore all files in a docs folder with the file extension .md: +#docs/*.md +# Then explicitly reverse the ignore rule for a single file: +#!docs/README.md + +api/** +docs/** +test/** +.travis.yml +git_push.sh +go.mod +go.sum +README.md \ No newline at end of file diff --git a/pkg/api/.openapi-generator/FILES b/pkg/api/.openapi-generator/FILES new file mode 100644 index 00000000..809c153f --- /dev/null +++ b/pkg/api/.openapi-generator/FILES @@ -0,0 +1,30 @@ +.gitignore +api_helm.go +api_kustomize.go +api_management.go +client.go +configuration.go +model_error_response.go +model_git_repository_path_reference.go +model_git_repository_reference.go +model_health_response.go +model_helm_chart_reference.go +model_helm_chart_repository_chart_reference.go +model_helm_chart_repository_reference.go +model_helm_get_chart_metadata_action.go +model_helm_get_chart_metadata_action_response.go +model_helm_list_charts_action.go +model_helm_list_charts_action_response.go +model_helm_render_chart_action.go +model_helm_render_chart_action_response.go +model_helm_render_metadata.go +model_helm_render_parameters.go +model_helm_repository_reference.go +model_kustomization_reference.go +model_kustomize_manifest_injection.go +model_kustomize_render_kustomization_action.go +model_kustomize_render_kustomization_action_response.go +model_kustomize_render_parameters.go +model_manifest.go +response.go +utils.go diff --git a/pkg/api/.openapi-generator/VERSION b/pkg/api/.openapi-generator/VERSION new file mode 100644 index 00000000..758bb9c8 --- /dev/null +++ b/pkg/api/.openapi-generator/VERSION @@ -0,0 +1 @@ +7.10.0 diff --git a/pkg/api/api_helm.go b/pkg/api/api_helm.go new file mode 100644 index 00000000..7103e657 --- /dev/null +++ b/pkg/api/api_helm.go @@ -0,0 +1,478 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" +) + + +// HelmAPIService HelmAPI service +type HelmAPIService service + +type ApiPostHelmGetChartMetadataActionRequest struct { + ctx context.Context + ApiService *HelmAPIService + helmGetChartMetadataAction *HelmGetChartMetadataAction +} + +func (r ApiPostHelmGetChartMetadataActionRequest) HelmGetChartMetadataAction(helmGetChartMetadataAction HelmGetChartMetadataAction) ApiPostHelmGetChartMetadataActionRequest { + r.helmGetChartMetadataAction = &helmGetChartMetadataAction + return r +} + +func (r ApiPostHelmGetChartMetadataActionRequest) Execute() (*HelmGetChartMetadataActionResponse, *http.Response, error) { + return r.ApiService.PostHelmGetChartMetadataActionExecute(r) +} + +/* +PostHelmGetChartMetadataAction Get Helm Chart Metadata + +Retrieves metadata of chart contained in the given repository. + +Service needs read permission on given repository. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiPostHelmGetChartMetadataActionRequest +*/ +func (a *HelmAPIService) PostHelmGetChartMetadataAction(ctx context.Context) ApiPostHelmGetChartMetadataActionRequest { + return ApiPostHelmGetChartMetadataActionRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return HelmGetChartMetadataActionResponse +func (a *HelmAPIService) PostHelmGetChartMetadataActionExecute(r ApiPostHelmGetChartMetadataActionRequest) (*HelmGetChartMetadataActionResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *HelmGetChartMetadataActionResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "HelmAPIService.PostHelmGetChartMetadataAction") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/rest/api/v1/helm/actions/get-chart-metadata" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.helmGetChartMetadataAction == nil { + return localVarReturnValue, nil, reportError("helmGetChartMetadataAction is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"*/*"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.helmGetChartMetadataAction + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 422 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 502 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiPostHelmListChartActionRequest struct { + ctx context.Context + ApiService *HelmAPIService + helmListChartsAction *HelmListChartsAction +} + +func (r ApiPostHelmListChartActionRequest) HelmListChartsAction(helmListChartsAction HelmListChartsAction) ApiPostHelmListChartActionRequest { + r.helmListChartsAction = &helmListChartsAction + return r +} + +func (r ApiPostHelmListChartActionRequest) Execute() (*HelmListChartsActionResponse, *http.Response, error) { + return r.ApiService.PostHelmListChartActionExecute(r) +} + +/* +PostHelmListChartAction List all Helm Charts + +Lists all Helm Charts contained in the given repository. + +Service needs read permission on given repository. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiPostHelmListChartActionRequest +*/ +func (a *HelmAPIService) PostHelmListChartAction(ctx context.Context) ApiPostHelmListChartActionRequest { + return ApiPostHelmListChartActionRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return HelmListChartsActionResponse +func (a *HelmAPIService) PostHelmListChartActionExecute(r ApiPostHelmListChartActionRequest) (*HelmListChartsActionResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *HelmListChartsActionResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "HelmAPIService.PostHelmListChartAction") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/rest/api/v1/helm/actions/list-charts" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.helmListChartsAction == nil { + return localVarReturnValue, nil, reportError("helmListChartsAction is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"*/*"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.helmListChartsAction + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 502 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiPostHelmRenderActionRequest struct { + ctx context.Context + ApiService *HelmAPIService + helmRenderChartAction *HelmRenderChartAction +} + +func (r ApiPostHelmRenderActionRequest) HelmRenderChartAction(helmRenderChartAction HelmRenderChartAction) ApiPostHelmRenderActionRequest { + r.helmRenderChartAction = &helmRenderChartAction + return r +} + +func (r ApiPostHelmRenderActionRequest) Execute() (*HelmRenderChartActionResponse, *http.Response, error) { + return r.ApiService.PostHelmRenderActionExecute(r) +} + +/* +PostHelmRenderAction Render Helm Chart + +Renders Kubernetes manifests based on Helm chart provided in a repository. + +Service needs read permission on given repository. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiPostHelmRenderActionRequest +*/ +func (a *HelmAPIService) PostHelmRenderAction(ctx context.Context) ApiPostHelmRenderActionRequest { + return ApiPostHelmRenderActionRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return HelmRenderChartActionResponse +func (a *HelmAPIService) PostHelmRenderActionExecute(r ApiPostHelmRenderActionRequest) (*HelmRenderChartActionResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *HelmRenderChartActionResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "HelmAPIService.PostHelmRenderAction") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/rest/api/v1/helm/actions/render-chart" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.helmRenderChartAction == nil { + return localVarReturnValue, nil, reportError("helmRenderChartAction is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"*/*"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.helmRenderChartAction + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 422 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 502 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/pkg/api/api_kustomize.go b/pkg/api/api_kustomize.go new file mode 100644 index 00000000..3a25e9c9 --- /dev/null +++ b/pkg/api/api_kustomize.go @@ -0,0 +1,179 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" +) + + +// KustomizeAPIService KustomizeAPI service +type KustomizeAPIService service + +type ApiPostKustomizeRenderKustomizationActionRequest struct { + ctx context.Context + ApiService *KustomizeAPIService + kustomizeRenderKustomizationAction *KustomizeRenderKustomizationAction +} + +func (r ApiPostKustomizeRenderKustomizationActionRequest) KustomizeRenderKustomizationAction(kustomizeRenderKustomizationAction KustomizeRenderKustomizationAction) ApiPostKustomizeRenderKustomizationActionRequest { + r.kustomizeRenderKustomizationAction = &kustomizeRenderKustomizationAction + return r +} + +func (r ApiPostKustomizeRenderKustomizationActionRequest) Execute() (*KustomizeRenderKustomizationActionResponse, *http.Response, error) { + return r.ApiService.PostKustomizeRenderKustomizationActionExecute(r) +} + +/* +PostKustomizeRenderKustomizationAction Render Kustomize Kustomization + +Renders Kubernetes manifests based on Kustomize Kustomization provided in repository. + +Service needs read permission on given repository. + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiPostKustomizeRenderKustomizationActionRequest +*/ +func (a *KustomizeAPIService) PostKustomizeRenderKustomizationAction(ctx context.Context) ApiPostKustomizeRenderKustomizationActionRequest { + return ApiPostKustomizeRenderKustomizationActionRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return KustomizeRenderKustomizationActionResponse +func (a *KustomizeAPIService) PostKustomizeRenderKustomizationActionExecute(r ApiPostKustomizeRenderKustomizationActionRequest) (*KustomizeRenderKustomizationActionResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodPost + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *KustomizeRenderKustomizationActionResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "KustomizeAPIService.PostKustomizeRenderKustomizationAction") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/rest/api/v1/kustomize/actions/render-kustomization" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + if r.kustomizeRenderKustomizationAction == nil { + return localVarReturnValue, nil, reportError("kustomizeRenderKustomizationAction is required and must be specified") + } + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{"application/json"} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"*/*"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + // body params + localVarPostBody = r.kustomizeRenderKustomizationAction + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 400 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 422 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + return localVarReturnValue, localVarHTTPResponse, newErr + } + if localVarHTTPResponse.StatusCode == 502 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/pkg/api/api_management.go b/pkg/api/api_management.go new file mode 100644 index 00000000..799a7f2b --- /dev/null +++ b/pkg/api/api_management.go @@ -0,0 +1,238 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "bytes" + "context" + "io" + "net/http" + "net/url" +) + + +// ManagementAPIService ManagementAPI service +type ManagementAPIService service + +type ApiGetLivenessHealthRequest struct { + ctx context.Context + ApiService *ManagementAPIService +} + +func (r ApiGetLivenessHealthRequest) Execute() (*HealthResponse, *http.Response, error) { + return r.ApiService.GetLivenessHealthExecute(r) +} + +/* +GetLivenessHealth Method for GetLivenessHealth + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiGetLivenessHealthRequest +*/ +func (a *ManagementAPIService) GetLivenessHealth(ctx context.Context) ApiGetLivenessHealthRequest { + return ApiGetLivenessHealthRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return HealthResponse +func (a *ManagementAPIService) GetLivenessHealthExecute(r ApiGetLivenessHealthRequest) (*HealthResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *HealthResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ManagementAPIService.GetLivenessHealth") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/health/liveness" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"*/*"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} + +type ApiGetReadinessHealthRequest struct { + ctx context.Context + ApiService *ManagementAPIService +} + +func (r ApiGetReadinessHealthRequest) Execute() (*HealthResponse, *http.Response, error) { + return r.ApiService.GetReadinessHealthExecute(r) +} + +/* +GetReadinessHealth Method for GetReadinessHealth + + @param ctx context.Context - for authentication, logging, cancellation, deadlines, tracing, etc. Passed from http.Request or context.Background(). + @return ApiGetReadinessHealthRequest +*/ +func (a *ManagementAPIService) GetReadinessHealth(ctx context.Context) ApiGetReadinessHealthRequest { + return ApiGetReadinessHealthRequest{ + ApiService: a, + ctx: ctx, + } +} + +// Execute executes the request +// @return HealthResponse +func (a *ManagementAPIService) GetReadinessHealthExecute(r ApiGetReadinessHealthRequest) (*HealthResponse, *http.Response, error) { + var ( + localVarHTTPMethod = http.MethodGet + localVarPostBody interface{} + formFiles []formFile + localVarReturnValue *HealthResponse + ) + + localBasePath, err := a.client.cfg.ServerURLWithContext(r.ctx, "ManagementAPIService.GetReadinessHealth") + if err != nil { + return localVarReturnValue, nil, &GenericOpenAPIError{error: err.Error()} + } + + localVarPath := localBasePath + "/health/readiness" + + localVarHeaderParams := make(map[string]string) + localVarQueryParams := url.Values{} + localVarFormParams := url.Values{} + + // to determine the Content-Type header + localVarHTTPContentTypes := []string{} + + // set Content-Type header + localVarHTTPContentType := selectHeaderContentType(localVarHTTPContentTypes) + if localVarHTTPContentType != "" { + localVarHeaderParams["Content-Type"] = localVarHTTPContentType + } + + // to determine the Accept header + localVarHTTPHeaderAccepts := []string{"*/*"} + + // set Accept header + localVarHTTPHeaderAccept := selectHeaderAccept(localVarHTTPHeaderAccepts) + if localVarHTTPHeaderAccept != "" { + localVarHeaderParams["Accept"] = localVarHTTPHeaderAccept + } + req, err := a.client.prepareRequest(r.ctx, localVarPath, localVarHTTPMethod, localVarPostBody, localVarHeaderParams, localVarQueryParams, localVarFormParams, formFiles) + if err != nil { + return localVarReturnValue, nil, err + } + + localVarHTTPResponse, err := a.client.callAPI(req) + if err != nil || localVarHTTPResponse == nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + localVarBody, err := io.ReadAll(localVarHTTPResponse.Body) + localVarHTTPResponse.Body.Close() + localVarHTTPResponse.Body = io.NopCloser(bytes.NewBuffer(localVarBody)) + if err != nil { + return localVarReturnValue, localVarHTTPResponse, err + } + + if localVarHTTPResponse.StatusCode >= 300 { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: localVarHTTPResponse.Status, + } + if localVarHTTPResponse.StatusCode == 500 { + var v ErrorResponse + err = a.client.decode(&v, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr.error = err.Error() + return localVarReturnValue, localVarHTTPResponse, newErr + } + newErr.error = formatErrorMessage(localVarHTTPResponse.Status, &v) + newErr.model = v + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + err = a.client.decode(&localVarReturnValue, localVarBody, localVarHTTPResponse.Header.Get("Content-Type")) + if err != nil { + newErr := &GenericOpenAPIError{ + body: localVarBody, + error: err.Error(), + } + return localVarReturnValue, localVarHTTPResponse, newErr + } + + return localVarReturnValue, localVarHTTPResponse, nil +} diff --git a/pkg/api/client.go b/pkg/api/client.go new file mode 100644 index 00000000..08b55ed6 --- /dev/null +++ b/pkg/api/client.go @@ -0,0 +1,659 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "bytes" + "context" + "encoding/json" + "encoding/xml" + "errors" + "fmt" + "io" + "log" + "mime/multipart" + "net/http" + "net/http/httputil" + "net/url" + "os" + "path/filepath" + "reflect" + "regexp" + "strconv" + "strings" + "time" + "unicode/utf8" + +) + +var ( + JsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:[^;]+\+)?json)`) + XmlCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:[^;]+\+)?xml)`) + queryParamSplit = regexp.MustCompile(`(^|&)([^&]+)`) + queryDescape = strings.NewReplacer( "%5B", "[", "%5D", "]" ) +) + +// APIClient manages communication with the Manifest Maestro API vv1 +// In most cases there should be only one, shared, APIClient. +type APIClient struct { + cfg *Configuration + common service // Reuse a single struct instead of allocating one for each service on the heap. + + // API Services + + HelmAPI *HelmAPIService + + KustomizeAPI *KustomizeAPIService + + ManagementAPI *ManagementAPIService +} + +type service struct { + client *APIClient +} + +// NewAPIClient creates a new API client. Requires a userAgent string describing your application. +// optionally a custom http.Client to allow for advanced features such as caching. +func NewAPIClient(cfg *Configuration) *APIClient { + if cfg.HTTPClient == nil { + cfg.HTTPClient = http.DefaultClient + } + + c := &APIClient{} + c.cfg = cfg + c.common.client = c + + // API Services + c.HelmAPI = (*HelmAPIService)(&c.common) + c.KustomizeAPI = (*KustomizeAPIService)(&c.common) + c.ManagementAPI = (*ManagementAPIService)(&c.common) + + return c +} + +func atoi(in string) (int, error) { + return strconv.Atoi(in) +} + +// selectHeaderContentType select a content type from the available list. +func selectHeaderContentType(contentTypes []string) string { + if len(contentTypes) == 0 { + return "" + } + if contains(contentTypes, "application/json") { + return "application/json" + } + return contentTypes[0] // use the first content type specified in 'consumes' +} + +// selectHeaderAccept join all accept types and return +func selectHeaderAccept(accepts []string) string { + if len(accepts) == 0 { + return "" + } + + if contains(accepts, "application/json") { + return "application/json" + } + + return strings.Join(accepts, ",") +} + +// contains is a case insensitive match, finding needle in a haystack +func contains(haystack []string, needle string) bool { + for _, a := range haystack { + if strings.EqualFold(a, needle) { + return true + } + } + return false +} + +// Verify optional parameters are of the correct type. +func typeCheckParameter(obj interface{}, expected string, name string) error { + // Make sure there is an object. + if obj == nil { + return nil + } + + // Check the type is as expected. + if reflect.TypeOf(obj).String() != expected { + return fmt.Errorf("expected %s to be of type %s but received %s", name, expected, reflect.TypeOf(obj).String()) + } + return nil +} + +func parameterValueToString( obj interface{}, key string ) string { + if reflect.TypeOf(obj).Kind() != reflect.Ptr { + return fmt.Sprintf("%v", obj) + } + var param,ok = obj.(MappedNullable) + if !ok { + return "" + } + dataMap,err := param.ToMap() + if err != nil { + return "" + } + return fmt.Sprintf("%v", dataMap[key]) +} + +// parameterAddToHeaderOrQuery adds the provided object to the request header or url query +// supporting deep object syntax +func parameterAddToHeaderOrQuery(headerOrQueryParams interface{}, keyPrefix string, obj interface{}, style string, collectionType string) { + var v = reflect.ValueOf(obj) + var value = "" + if v == reflect.ValueOf(nil) { + value = "null" + } else { + switch v.Kind() { + case reflect.Invalid: + value = "invalid" + + case reflect.Struct: + if t,ok := obj.(MappedNullable); ok { + dataMap,err := t.ToMap() + if err != nil { + return + } + parameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, dataMap, style, collectionType) + return + } + if t, ok := obj.(time.Time); ok { + parameterAddToHeaderOrQuery(headerOrQueryParams, keyPrefix, t.Format(time.RFC3339Nano), style, collectionType) + return + } + value = v.Type().String() + " value" + case reflect.Slice: + var indValue = reflect.ValueOf(obj) + if indValue == reflect.ValueOf(nil) { + return + } + var lenIndValue = indValue.Len() + for i:=0;i 0 || (len(formFiles) > 0) { + if body != nil { + return nil, errors.New("Cannot specify postBody and multipart form at the same time.") + } + body = &bytes.Buffer{} + w := multipart.NewWriter(body) + + for k, v := range formParams { + for _, iv := range v { + if strings.HasPrefix(k, "@") { // file + err = addFile(w, k[1:], iv) + if err != nil { + return nil, err + } + } else { // form value + w.WriteField(k, iv) + } + } + } + for _, formFile := range formFiles { + if len(formFile.fileBytes) > 0 && formFile.fileName != "" { + w.Boundary() + part, err := w.CreateFormFile(formFile.formFileName, filepath.Base(formFile.fileName)) + if err != nil { + return nil, err + } + _, err = part.Write(formFile.fileBytes) + if err != nil { + return nil, err + } + } + } + + // Set the Boundary in the Content-Type + headerParams["Content-Type"] = w.FormDataContentType() + + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + w.Close() + } + + if strings.HasPrefix(headerParams["Content-Type"], "application/x-www-form-urlencoded") && len(formParams) > 0 { + if body != nil { + return nil, errors.New("Cannot specify postBody and x-www-form-urlencoded form at the same time.") + } + body = &bytes.Buffer{} + body.WriteString(formParams.Encode()) + // Set Content-Length + headerParams["Content-Length"] = fmt.Sprintf("%d", body.Len()) + } + + // Setup path and query parameters + url, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Override request host, if applicable + if c.cfg.Host != "" { + url.Host = c.cfg.Host + } + + // Override request scheme, if applicable + if c.cfg.Scheme != "" { + url.Scheme = c.cfg.Scheme + } + + // Adding Query Param + query := url.Query() + for k, v := range queryParams { + for _, iv := range v { + query.Add(k, iv) + } + } + + // Encode the parameters. + url.RawQuery = queryParamSplit.ReplaceAllStringFunc(query.Encode(), func(s string) string { + pieces := strings.Split(s, "=") + pieces[0] = queryDescape.Replace(pieces[0]) + return strings.Join(pieces, "=") + }) + + // Generate a new request + if body != nil { + localVarRequest, err = http.NewRequest(method, url.String(), body) + } else { + localVarRequest, err = http.NewRequest(method, url.String(), nil) + } + if err != nil { + return nil, err + } + + // add header parameters, if any + if len(headerParams) > 0 { + headers := http.Header{} + for h, v := range headerParams { + headers[h] = []string{v} + } + localVarRequest.Header = headers + } + + // Add the user agent to the request. + localVarRequest.Header.Add("User-Agent", c.cfg.UserAgent) + + if ctx != nil { + // add context to the request + localVarRequest = localVarRequest.WithContext(ctx) + + // Walk through any authentication. + + } + + for header, value := range c.cfg.DefaultHeader { + localVarRequest.Header.Add(header, value) + } + return localVarRequest, nil +} + +func (c *APIClient) decode(v interface{}, b []byte, contentType string) (err error) { + if len(b) == 0 { + return nil + } + if s, ok := v.(*string); ok { + *s = string(b) + return nil + } + if f, ok := v.(*os.File); ok { + f, err = os.CreateTemp("", "HttpClientFile") + if err != nil { + return + } + _, err = f.Write(b) + if err != nil { + return + } + _, err = f.Seek(0, io.SeekStart) + return + } + if f, ok := v.(**os.File); ok { + *f, err = os.CreateTemp("", "HttpClientFile") + if err != nil { + return + } + _, err = (*f).Write(b) + if err != nil { + return + } + _, err = (*f).Seek(0, io.SeekStart) + return + } + if XmlCheck.MatchString(contentType) { + if err = xml.Unmarshal(b, v); err != nil { + return err + } + return nil + } + if JsonCheck.MatchString(contentType) { + if actualObj, ok := v.(interface{ GetActualInstance() interface{} }); ok { // oneOf, anyOf schemas + if unmarshalObj, ok := actualObj.(interface{ UnmarshalJSON([]byte) error }); ok { // make sure it has UnmarshalJSON defined + if err = unmarshalObj.UnmarshalJSON(b); err != nil { + return err + } + } else { + return errors.New("Unknown type with GetActualInstance but no unmarshalObj.UnmarshalJSON defined") + } + } else if err = json.Unmarshal(b, v); err != nil { // simple model + return err + } + return nil + } + return errors.New("undefined response type") +} + +// Add a file to the multipart request +func addFile(w *multipart.Writer, fieldName, path string) error { + file, err := os.Open(filepath.Clean(path)) + if err != nil { + return err + } + err = file.Close() + if err != nil { + return err + } + + part, err := w.CreateFormFile(fieldName, filepath.Base(path)) + if err != nil { + return err + } + _, err = io.Copy(part, file) + + return err +} + +// Set request body from an interface{} +func setBody(body interface{}, contentType string) (bodyBuf *bytes.Buffer, err error) { + if bodyBuf == nil { + bodyBuf = &bytes.Buffer{} + } + + if reader, ok := body.(io.Reader); ok { + _, err = bodyBuf.ReadFrom(reader) + } else if fp, ok := body.(*os.File); ok { + _, err = bodyBuf.ReadFrom(fp) + } else if b, ok := body.([]byte); ok { + _, err = bodyBuf.Write(b) + } else if s, ok := body.(string); ok { + _, err = bodyBuf.WriteString(s) + } else if s, ok := body.(*string); ok { + _, err = bodyBuf.WriteString(*s) + } else if JsonCheck.MatchString(contentType) { + err = json.NewEncoder(bodyBuf).Encode(body) + } else if XmlCheck.MatchString(contentType) { + var bs []byte + bs, err = xml.Marshal(body) + if err == nil { + bodyBuf.Write(bs) + } + } + + if err != nil { + return nil, err + } + + if bodyBuf.Len() == 0 { + err = fmt.Errorf("invalid body type %s\n", contentType) + return nil, err + } + return bodyBuf, nil +} + +// detectContentType method is used to figure out `Request.Body` content type for request header +func detectContentType(body interface{}) string { + contentType := "text/plain; charset=utf-8" + kind := reflect.TypeOf(body).Kind() + + switch kind { + case reflect.Struct, reflect.Map, reflect.Ptr: + contentType = "application/json; charset=utf-8" + case reflect.String: + contentType = "text/plain; charset=utf-8" + default: + if b, ok := body.([]byte); ok { + contentType = http.DetectContentType(b) + } else if kind == reflect.Slice { + contentType = "application/json; charset=utf-8" + } + } + + return contentType +} + +// Ripped from https://github.com/gregjones/httpcache/blob/master/httpcache.go +type cacheControl map[string]string + +func parseCacheControl(headers http.Header) cacheControl { + cc := cacheControl{} + ccHeader := headers.Get("Cache-Control") + for _, part := range strings.Split(ccHeader, ",") { + part = strings.Trim(part, " ") + if part == "" { + continue + } + if strings.ContainsRune(part, '=') { + keyval := strings.Split(part, "=") + cc[strings.Trim(keyval[0], " ")] = strings.Trim(keyval[1], ",") + } else { + cc[part] = "" + } + } + return cc +} + +// CacheExpires helper function to determine remaining time before repeating a request. +func CacheExpires(r *http.Response) time.Time { + // Figure out when the cache expires. + var expires time.Time + now, err := time.Parse(time.RFC1123, r.Header.Get("date")) + if err != nil { + return time.Now() + } + respCacheControl := parseCacheControl(r.Header) + + if maxAge, ok := respCacheControl["max-age"]; ok { + lifetime, err := time.ParseDuration(maxAge + "s") + if err != nil { + expires = now + } else { + expires = now.Add(lifetime) + } + } else { + expiresHeader := r.Header.Get("Expires") + if expiresHeader != "" { + expires, err = time.Parse(time.RFC1123, expiresHeader) + if err != nil { + expires = now + } + } + } + return expires +} + +func strlen(s string) int { + return utf8.RuneCountInString(s) +} + +// GenericOpenAPIError Provides access to the body, error and model on returned errors. +type GenericOpenAPIError struct { + body []byte + error string + model interface{} +} + +// Error returns non-empty string if there was an error. +func (e GenericOpenAPIError) Error() string { + return e.error +} + +// Body returns the raw bytes of the response +func (e GenericOpenAPIError) Body() []byte { + return e.body +} + +// Model returns the unpacked model of the error +func (e GenericOpenAPIError) Model() interface{} { + return e.model +} + +// format error message using title and detail when model implements rfc7807 +func formatErrorMessage(status string, v interface{}) string { + str := "" + metaValue := reflect.ValueOf(v).Elem() + + if metaValue.Kind() == reflect.Struct { + field := metaValue.FieldByName("Title") + if field != (reflect.Value{}) { + str = fmt.Sprintf("%s", field.Interface()) + } + + field = metaValue.FieldByName("Detail") + if field != (reflect.Value{}) { + str = fmt.Sprintf("%s (%s)", str, field.Interface()) + } + } + + return strings.TrimSpace(fmt.Sprintf("%s %s", status, str)) +} diff --git a/pkg/api/configuration.go b/pkg/api/configuration.go new file mode 100644 index 00000000..364945b1 --- /dev/null +++ b/pkg/api/configuration.go @@ -0,0 +1,216 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "context" + "fmt" + "net/http" + "strings" +) + +// contextKeys are used to identify the type of value in the context. +// Since these are string, it is possible to get a short description of the +// context key for logging and debugging using key.String(). + +type contextKey string + +func (c contextKey) String() string { + return "auth " + string(c) +} + +var ( + // ContextServerIndex uses a server configuration from the index. + ContextServerIndex = contextKey("serverIndex") + + // ContextOperationServerIndices uses a server configuration from the index mapping. + ContextOperationServerIndices = contextKey("serverOperationIndices") + + // ContextServerVariables overrides a server configuration variables. + ContextServerVariables = contextKey("serverVariables") + + // ContextOperationServerVariables overrides a server configuration variables using operation specific values. + ContextOperationServerVariables = contextKey("serverOperationVariables") +) + +// BasicAuth provides basic http authentication to a request passed via context using ContextBasicAuth +type BasicAuth struct { + UserName string `json:"userName,omitempty"` + Password string `json:"password,omitempty"` +} + +// APIKey provides API key based authentication to a request passed via context using ContextAPIKey +type APIKey struct { + Key string + Prefix string +} + +// ServerVariable stores the information about a server variable +type ServerVariable struct { + Description string + DefaultValue string + EnumValues []string +} + +// ServerConfiguration stores the information about a server +type ServerConfiguration struct { + URL string + Description string + Variables map[string]ServerVariable +} + +// ServerConfigurations stores multiple ServerConfiguration items +type ServerConfigurations []ServerConfiguration + +// Configuration stores the configuration of the API client +type Configuration struct { + Host string `json:"host,omitempty"` + Scheme string `json:"scheme,omitempty"` + DefaultHeader map[string]string `json:"defaultHeader,omitempty"` + UserAgent string `json:"userAgent,omitempty"` + Debug bool `json:"debug,omitempty"` + Servers ServerConfigurations + OperationServers map[string]ServerConfigurations + HTTPClient *http.Client +} + +// NewConfiguration returns a new Configuration object +func NewConfiguration() *Configuration { + cfg := &Configuration{ + DefaultHeader: make(map[string]string), + UserAgent: "OpenAPI-Generator/1.0.0/go", + Debug: false, + Servers: ServerConfigurations{ + { + URL: "", + Description: "No description provided", + }, + }, + OperationServers: map[string]ServerConfigurations{ + }, + } + return cfg +} + +// AddDefaultHeader adds a new HTTP header to the default header in the request +func (c *Configuration) AddDefaultHeader(key string, value string) { + c.DefaultHeader[key] = value +} + +// URL formats template on a index using given variables +func (sc ServerConfigurations) URL(index int, variables map[string]string) (string, error) { + if index < 0 || len(sc) <= index { + return "", fmt.Errorf("index %v out of range %v", index, len(sc)-1) + } + server := sc[index] + url := server.URL + + // go through variables and replace placeholders + for name, variable := range server.Variables { + if value, ok := variables[name]; ok { + found := bool(len(variable.EnumValues) == 0) + for _, enumValue := range variable.EnumValues { + if value == enumValue { + found = true + } + } + if !found { + return "", fmt.Errorf("the variable %s in the server URL has invalid value %v. Must be %v", name, value, variable.EnumValues) + } + url = strings.Replace(url, "{"+name+"}", value, -1) + } else { + url = strings.Replace(url, "{"+name+"}", variable.DefaultValue, -1) + } + } + return url, nil +} + +// ServerURL returns URL based on server settings +func (c *Configuration) ServerURL(index int, variables map[string]string) (string, error) { + return c.Servers.URL(index, variables) +} + +func getServerIndex(ctx context.Context) (int, error) { + si := ctx.Value(ContextServerIndex) + if si != nil { + if index, ok := si.(int); ok { + return index, nil + } + return 0, reportError("Invalid type %T should be int", si) + } + return 0, nil +} + +func getServerOperationIndex(ctx context.Context, endpoint string) (int, error) { + osi := ctx.Value(ContextOperationServerIndices) + if osi != nil { + if operationIndices, ok := osi.(map[string]int); !ok { + return 0, reportError("Invalid type %T should be map[string]int", osi) + } else { + index, ok := operationIndices[endpoint] + if ok { + return index, nil + } + } + } + return getServerIndex(ctx) +} + +func getServerVariables(ctx context.Context) (map[string]string, error) { + sv := ctx.Value(ContextServerVariables) + if sv != nil { + if variables, ok := sv.(map[string]string); ok { + return variables, nil + } + return nil, reportError("ctx value of ContextServerVariables has invalid type %T should be map[string]string", sv) + } + return nil, nil +} + +func getServerOperationVariables(ctx context.Context, endpoint string) (map[string]string, error) { + osv := ctx.Value(ContextOperationServerVariables) + if osv != nil { + if operationVariables, ok := osv.(map[string]map[string]string); !ok { + return nil, reportError("ctx value of ContextOperationServerVariables has invalid type %T should be map[string]map[string]string", osv) + } else { + variables, ok := operationVariables[endpoint] + if ok { + return variables, nil + } + } + } + return getServerVariables(ctx) +} + +// ServerURLWithContext returns a new server URL given an endpoint +func (c *Configuration) ServerURLWithContext(ctx context.Context, endpoint string) (string, error) { + sc, ok := c.OperationServers[endpoint] + if !ok { + sc = c.Servers + } + + if ctx == nil { + return sc.URL(0, nil) + } + + index, err := getServerOperationIndex(ctx, endpoint) + if err != nil { + return "", err + } + + variables, err := getServerOperationVariables(ctx, endpoint) + if err != nil { + return "", err + } + + return sc.URL(index, variables) +} diff --git a/pkg/api/model_error_response.go b/pkg/api/model_error_response.go new file mode 100644 index 00000000..d77a8a7f --- /dev/null +++ b/pkg/api/model_error_response.go @@ -0,0 +1,271 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// checks if the ErrorResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &ErrorResponse{} + +// ErrorResponse struct for ErrorResponse +type ErrorResponse struct { + Type *string `json:"type,omitempty"` + Title *string `json:"title,omitempty"` + Status *float32 `json:"status,omitempty"` + Detail *string `json:"detail,omitempty"` + Instance *string `json:"instance,omitempty"` +} + +// NewErrorResponse instantiates a new ErrorResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewErrorResponse() *ErrorResponse { + this := ErrorResponse{} + return &this +} + +// NewErrorResponseWithDefaults instantiates a new ErrorResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewErrorResponseWithDefaults() *ErrorResponse { + this := ErrorResponse{} + return &this +} + +// GetType returns the Type field value if set, zero value otherwise. +func (o *ErrorResponse) GetType() string { + if o == nil || IsNil(o.Type) { + var ret string + return ret + } + return *o.Type +} + +// GetTypeOk returns a tuple with the Type field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ErrorResponse) GetTypeOk() (*string, bool) { + if o == nil || IsNil(o.Type) { + return nil, false + } + return o.Type, true +} + +// HasType returns a boolean if a field has been set. +func (o *ErrorResponse) HasType() bool { + if o != nil && !IsNil(o.Type) { + return true + } + + return false +} + +// SetType gets a reference to the given string and assigns it to the Type field. +func (o *ErrorResponse) SetType(v string) { + o.Type = &v +} + +// GetTitle returns the Title field value if set, zero value otherwise. +func (o *ErrorResponse) GetTitle() string { + if o == nil || IsNil(o.Title) { + var ret string + return ret + } + return *o.Title +} + +// GetTitleOk returns a tuple with the Title field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ErrorResponse) GetTitleOk() (*string, bool) { + if o == nil || IsNil(o.Title) { + return nil, false + } + return o.Title, true +} + +// HasTitle returns a boolean if a field has been set. +func (o *ErrorResponse) HasTitle() bool { + if o != nil && !IsNil(o.Title) { + return true + } + + return false +} + +// SetTitle gets a reference to the given string and assigns it to the Title field. +func (o *ErrorResponse) SetTitle(v string) { + o.Title = &v +} + +// GetStatus returns the Status field value if set, zero value otherwise. +func (o *ErrorResponse) GetStatus() float32 { + if o == nil || IsNil(o.Status) { + var ret float32 + return ret + } + return *o.Status +} + +// GetStatusOk returns a tuple with the Status field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ErrorResponse) GetStatusOk() (*float32, bool) { + if o == nil || IsNil(o.Status) { + return nil, false + } + return o.Status, true +} + +// HasStatus returns a boolean if a field has been set. +func (o *ErrorResponse) HasStatus() bool { + if o != nil && !IsNil(o.Status) { + return true + } + + return false +} + +// SetStatus gets a reference to the given float32 and assigns it to the Status field. +func (o *ErrorResponse) SetStatus(v float32) { + o.Status = &v +} + +// GetDetail returns the Detail field value if set, zero value otherwise. +func (o *ErrorResponse) GetDetail() string { + if o == nil || IsNil(o.Detail) { + var ret string + return ret + } + return *o.Detail +} + +// GetDetailOk returns a tuple with the Detail field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ErrorResponse) GetDetailOk() (*string, bool) { + if o == nil || IsNil(o.Detail) { + return nil, false + } + return o.Detail, true +} + +// HasDetail returns a boolean if a field has been set. +func (o *ErrorResponse) HasDetail() bool { + if o != nil && !IsNil(o.Detail) { + return true + } + + return false +} + +// SetDetail gets a reference to the given string and assigns it to the Detail field. +func (o *ErrorResponse) SetDetail(v string) { + o.Detail = &v +} + +// GetInstance returns the Instance field value if set, zero value otherwise. +func (o *ErrorResponse) GetInstance() string { + if o == nil || IsNil(o.Instance) { + var ret string + return ret + } + return *o.Instance +} + +// GetInstanceOk returns a tuple with the Instance field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *ErrorResponse) GetInstanceOk() (*string, bool) { + if o == nil || IsNil(o.Instance) { + return nil, false + } + return o.Instance, true +} + +// HasInstance returns a boolean if a field has been set. +func (o *ErrorResponse) HasInstance() bool { + if o != nil && !IsNil(o.Instance) { + return true + } + + return false +} + +// SetInstance gets a reference to the given string and assigns it to the Instance field. +func (o *ErrorResponse) SetInstance(v string) { + o.Instance = &v +} + +func (o ErrorResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o ErrorResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Type) { + toSerialize["type"] = o.Type + } + if !IsNil(o.Title) { + toSerialize["title"] = o.Title + } + if !IsNil(o.Status) { + toSerialize["status"] = o.Status + } + if !IsNil(o.Detail) { + toSerialize["detail"] = o.Detail + } + if !IsNil(o.Instance) { + toSerialize["instance"] = o.Instance + } + return toSerialize, nil +} + +type NullableErrorResponse struct { + value *ErrorResponse + isSet bool +} + +func (v NullableErrorResponse) Get() *ErrorResponse { + return v.value +} + +func (v *NullableErrorResponse) Set(val *ErrorResponse) { + v.value = val + v.isSet = true +} + +func (v NullableErrorResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableErrorResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableErrorResponse(val *ErrorResponse) *NullableErrorResponse { + return &NullableErrorResponse{value: val, isSet: true} +} + +func (v NullableErrorResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableErrorResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_git_repository_path_reference.go b/pkg/api/model_git_repository_path_reference.go new file mode 100644 index 00000000..fe35956e --- /dev/null +++ b/pkg/api/model_git_repository_path_reference.go @@ -0,0 +1,251 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the GitRepositoryPathReference type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &GitRepositoryPathReference{} + +// GitRepositoryPathReference struct for GitRepositoryPathReference +type GitRepositoryPathReference struct { + RepositoryType string `json:"repositoryType"` + RepositoryURL string `json:"repositoryURL"` + GitReference string `json:"gitReference"` + Path *string `json:"path,omitempty"` +} + +type _GitRepositoryPathReference GitRepositoryPathReference + +// NewGitRepositoryPathReference instantiates a new GitRepositoryPathReference object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewGitRepositoryPathReference(repositoryType string, repositoryURL string, gitReference string) *GitRepositoryPathReference { + this := GitRepositoryPathReference{} + this.RepositoryType = repositoryType + this.RepositoryURL = repositoryURL + this.GitReference = gitReference + return &this +} + +// NewGitRepositoryPathReferenceWithDefaults instantiates a new GitRepositoryPathReference object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewGitRepositoryPathReferenceWithDefaults() *GitRepositoryPathReference { + this := GitRepositoryPathReference{} + return &this +} + +// GetRepositoryType returns the RepositoryType field value +func (o *GitRepositoryPathReference) GetRepositoryType() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryType +} + +// GetRepositoryTypeOk returns a tuple with the RepositoryType field value +// and a boolean to check if the value has been set. +func (o *GitRepositoryPathReference) GetRepositoryTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryType, true +} + +// SetRepositoryType sets field value +func (o *GitRepositoryPathReference) SetRepositoryType(v string) { + o.RepositoryType = v +} + +// GetRepositoryURL returns the RepositoryURL field value +func (o *GitRepositoryPathReference) GetRepositoryURL() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryURL +} + +// GetRepositoryURLOk returns a tuple with the RepositoryURL field value +// and a boolean to check if the value has been set. +func (o *GitRepositoryPathReference) GetRepositoryURLOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryURL, true +} + +// SetRepositoryURL sets field value +func (o *GitRepositoryPathReference) SetRepositoryURL(v string) { + o.RepositoryURL = v +} + +// GetGitReference returns the GitReference field value +func (o *GitRepositoryPathReference) GetGitReference() string { + if o == nil { + var ret string + return ret + } + + return o.GitReference +} + +// GetGitReferenceOk returns a tuple with the GitReference field value +// and a boolean to check if the value has been set. +func (o *GitRepositoryPathReference) GetGitReferenceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.GitReference, true +} + +// SetGitReference sets field value +func (o *GitRepositoryPathReference) SetGitReference(v string) { + o.GitReference = v +} + +// GetPath returns the Path field value if set, zero value otherwise. +func (o *GitRepositoryPathReference) GetPath() string { + if o == nil || IsNil(o.Path) { + var ret string + return ret + } + return *o.Path +} + +// GetPathOk returns a tuple with the Path field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *GitRepositoryPathReference) GetPathOk() (*string, bool) { + if o == nil || IsNil(o.Path) { + return nil, false + } + return o.Path, true +} + +// HasPath returns a boolean if a field has been set. +func (o *GitRepositoryPathReference) HasPath() bool { + if o != nil && !IsNil(o.Path) { + return true + } + + return false +} + +// SetPath gets a reference to the given string and assigns it to the Path field. +func (o *GitRepositoryPathReference) SetPath(v string) { + o.Path = &v +} + +func (o GitRepositoryPathReference) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o GitRepositoryPathReference) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["repositoryType"] = o.RepositoryType + toSerialize["repositoryURL"] = o.RepositoryURL + toSerialize["gitReference"] = o.GitReference + if !IsNil(o.Path) { + toSerialize["path"] = o.Path + } + return toSerialize, nil +} + +func (o *GitRepositoryPathReference) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "repositoryType", + "repositoryURL", + "gitReference", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varGitRepositoryPathReference := _GitRepositoryPathReference{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varGitRepositoryPathReference) + + if err != nil { + return err + } + + *o = GitRepositoryPathReference(varGitRepositoryPathReference) + + return err +} + +type NullableGitRepositoryPathReference struct { + value *GitRepositoryPathReference + isSet bool +} + +func (v NullableGitRepositoryPathReference) Get() *GitRepositoryPathReference { + return v.value +} + +func (v *NullableGitRepositoryPathReference) Set(val *GitRepositoryPathReference) { + v.value = val + v.isSet = true +} + +func (v NullableGitRepositoryPathReference) IsSet() bool { + return v.isSet +} + +func (v *NullableGitRepositoryPathReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableGitRepositoryPathReference(val *GitRepositoryPathReference) *NullableGitRepositoryPathReference { + return &NullableGitRepositoryPathReference{value: val, isSet: true} +} + +func (v NullableGitRepositoryPathReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableGitRepositoryPathReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_git_repository_reference.go b/pkg/api/model_git_repository_reference.go new file mode 100644 index 00000000..f0787111 --- /dev/null +++ b/pkg/api/model_git_repository_reference.go @@ -0,0 +1,215 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the GitRepositoryReference type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &GitRepositoryReference{} + +// GitRepositoryReference struct for GitRepositoryReference +type GitRepositoryReference struct { + RepositoryType string `json:"repositoryType"` + RepositoryURL string `json:"repositoryURL"` + GitReference string `json:"gitReference"` +} + +type _GitRepositoryReference GitRepositoryReference + +// NewGitRepositoryReference instantiates a new GitRepositoryReference object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewGitRepositoryReference(repositoryType string, repositoryURL string, gitReference string) *GitRepositoryReference { + this := GitRepositoryReference{} + this.RepositoryType = repositoryType + this.RepositoryURL = repositoryURL + this.GitReference = gitReference + return &this +} + +// NewGitRepositoryReferenceWithDefaults instantiates a new GitRepositoryReference object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewGitRepositoryReferenceWithDefaults() *GitRepositoryReference { + this := GitRepositoryReference{} + return &this +} + +// GetRepositoryType returns the RepositoryType field value +func (o *GitRepositoryReference) GetRepositoryType() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryType +} + +// GetRepositoryTypeOk returns a tuple with the RepositoryType field value +// and a boolean to check if the value has been set. +func (o *GitRepositoryReference) GetRepositoryTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryType, true +} + +// SetRepositoryType sets field value +func (o *GitRepositoryReference) SetRepositoryType(v string) { + o.RepositoryType = v +} + +// GetRepositoryURL returns the RepositoryURL field value +func (o *GitRepositoryReference) GetRepositoryURL() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryURL +} + +// GetRepositoryURLOk returns a tuple with the RepositoryURL field value +// and a boolean to check if the value has been set. +func (o *GitRepositoryReference) GetRepositoryURLOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryURL, true +} + +// SetRepositoryURL sets field value +func (o *GitRepositoryReference) SetRepositoryURL(v string) { + o.RepositoryURL = v +} + +// GetGitReference returns the GitReference field value +func (o *GitRepositoryReference) GetGitReference() string { + if o == nil { + var ret string + return ret + } + + return o.GitReference +} + +// GetGitReferenceOk returns a tuple with the GitReference field value +// and a boolean to check if the value has been set. +func (o *GitRepositoryReference) GetGitReferenceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.GitReference, true +} + +// SetGitReference sets field value +func (o *GitRepositoryReference) SetGitReference(v string) { + o.GitReference = v +} + +func (o GitRepositoryReference) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o GitRepositoryReference) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["repositoryType"] = o.RepositoryType + toSerialize["repositoryURL"] = o.RepositoryURL + toSerialize["gitReference"] = o.GitReference + return toSerialize, nil +} + +func (o *GitRepositoryReference) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "repositoryType", + "repositoryURL", + "gitReference", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varGitRepositoryReference := _GitRepositoryReference{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varGitRepositoryReference) + + if err != nil { + return err + } + + *o = GitRepositoryReference(varGitRepositoryReference) + + return err +} + +type NullableGitRepositoryReference struct { + value *GitRepositoryReference + isSet bool +} + +func (v NullableGitRepositoryReference) Get() *GitRepositoryReference { + return v.value +} + +func (v *NullableGitRepositoryReference) Set(val *GitRepositoryReference) { + v.value = val + v.isSet = true +} + +func (v NullableGitRepositoryReference) IsSet() bool { + return v.isSet +} + +func (v *NullableGitRepositoryReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableGitRepositoryReference(val *GitRepositoryReference) *NullableGitRepositoryReference { + return &NullableGitRepositoryReference{value: val, isSet: true} +} + +func (v NullableGitRepositoryReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableGitRepositoryReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_health_response.go b/pkg/api/model_health_response.go new file mode 100644 index 00000000..2f33e306 --- /dev/null +++ b/pkg/api/model_health_response.go @@ -0,0 +1,163 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// checks if the HealthResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HealthResponse{} + +// HealthResponse struct for HealthResponse +type HealthResponse struct { + Description *string `json:"description,omitempty"` + Status *string `json:"status,omitempty"` +} + +// NewHealthResponse instantiates a new HealthResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHealthResponse() *HealthResponse { + this := HealthResponse{} + return &this +} + +// NewHealthResponseWithDefaults instantiates a new HealthResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHealthResponseWithDefaults() *HealthResponse { + this := HealthResponse{} + return &this +} + +// GetDescription returns the Description field value if set, zero value otherwise. +func (o *HealthResponse) GetDescription() string { + if o == nil || IsNil(o.Description) { + var ret string + return ret + } + return *o.Description +} + +// GetDescriptionOk returns a tuple with the Description field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HealthResponse) GetDescriptionOk() (*string, bool) { + if o == nil || IsNil(o.Description) { + return nil, false + } + return o.Description, true +} + +// HasDescription returns a boolean if a field has been set. +func (o *HealthResponse) HasDescription() bool { + if o != nil && !IsNil(o.Description) { + return true + } + + return false +} + +// SetDescription gets a reference to the given string and assigns it to the Description field. +func (o *HealthResponse) SetDescription(v string) { + o.Description = &v +} + +// GetStatus returns the Status field value if set, zero value otherwise. +func (o *HealthResponse) GetStatus() string { + if o == nil || IsNil(o.Status) { + var ret string + return ret + } + return *o.Status +} + +// GetStatusOk returns a tuple with the Status field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HealthResponse) GetStatusOk() (*string, bool) { + if o == nil || IsNil(o.Status) { + return nil, false + } + return o.Status, true +} + +// HasStatus returns a boolean if a field has been set. +func (o *HealthResponse) HasStatus() bool { + if o != nil && !IsNil(o.Status) { + return true + } + + return false +} + +// SetStatus gets a reference to the given string and assigns it to the Status field. +func (o *HealthResponse) SetStatus(v string) { + o.Status = &v +} + +func (o HealthResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HealthResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Description) { + toSerialize["description"] = o.Description + } + if !IsNil(o.Status) { + toSerialize["status"] = o.Status + } + return toSerialize, nil +} + +type NullableHealthResponse struct { + value *HealthResponse + isSet bool +} + +func (v NullableHealthResponse) Get() *HealthResponse { + return v.value +} + +func (v *NullableHealthResponse) Set(val *HealthResponse) { + v.value = val + v.isSet = true +} + +func (v NullableHealthResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableHealthResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHealthResponse(val *HealthResponse) *NullableHealthResponse { + return &NullableHealthResponse{value: val, isSet: true} +} + +func (v NullableHealthResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHealthResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_chart_reference.go b/pkg/api/model_helm_chart_reference.go new file mode 100644 index 00000000..a5e7881b --- /dev/null +++ b/pkg/api/model_helm_chart_reference.go @@ -0,0 +1,158 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "gopkg.in/validator.v2" + "fmt" +) + +// HelmChartReference - struct for HelmChartReference +type HelmChartReference struct { + GitRepositoryPathReference *GitRepositoryPathReference + HelmChartRepositoryChartReference *HelmChartRepositoryChartReference +} + +// GitRepositoryPathReferenceAsHelmChartReference is a convenience function that returns GitRepositoryPathReference wrapped in HelmChartReference +func GitRepositoryPathReferenceAsHelmChartReference(v *GitRepositoryPathReference) HelmChartReference { + return HelmChartReference{ + GitRepositoryPathReference: v, + } +} + +// HelmChartRepositoryChartReferenceAsHelmChartReference is a convenience function that returns HelmChartRepositoryChartReference wrapped in HelmChartReference +func HelmChartRepositoryChartReferenceAsHelmChartReference(v *HelmChartRepositoryChartReference) HelmChartReference { + return HelmChartReference{ + HelmChartRepositoryChartReference: v, + } +} + + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *HelmChartReference) UnmarshalJSON(data []byte) error { + var err error + match := 0 + // try to unmarshal data into GitRepositoryPathReference + err = newStrictDecoder(data).Decode(&dst.GitRepositoryPathReference) + if err == nil { + jsonGitRepositoryPathReference, _ := json.Marshal(dst.GitRepositoryPathReference) + if string(jsonGitRepositoryPathReference) == "{}" { // empty struct + dst.GitRepositoryPathReference = nil + } else { + if err = validator.Validate(dst.GitRepositoryPathReference); err != nil { + dst.GitRepositoryPathReference = nil + } else { + match++ + } + } + } else { + dst.GitRepositoryPathReference = nil + } + + // try to unmarshal data into HelmChartRepositoryChartReference + err = newStrictDecoder(data).Decode(&dst.HelmChartRepositoryChartReference) + if err == nil { + jsonHelmChartRepositoryChartReference, _ := json.Marshal(dst.HelmChartRepositoryChartReference) + if string(jsonHelmChartRepositoryChartReference) == "{}" { // empty struct + dst.HelmChartRepositoryChartReference = nil + } else { + if err = validator.Validate(dst.HelmChartRepositoryChartReference); err != nil { + dst.HelmChartRepositoryChartReference = nil + } else { + match++ + } + } + } else { + dst.HelmChartRepositoryChartReference = nil + } + + if match > 1 { // more than 1 match + // reset to nil + dst.GitRepositoryPathReference = nil + dst.HelmChartRepositoryChartReference = nil + + return fmt.Errorf("data matches more than one schema in oneOf(HelmChartReference)") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("data failed to match schemas in oneOf(HelmChartReference)") + } +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src HelmChartReference) MarshalJSON() ([]byte, error) { + if src.GitRepositoryPathReference != nil { + return json.Marshal(&src.GitRepositoryPathReference) + } + + if src.HelmChartRepositoryChartReference != nil { + return json.Marshal(&src.HelmChartRepositoryChartReference) + } + + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *HelmChartReference) GetActualInstance() (interface{}) { + if obj == nil { + return nil + } + if obj.GitRepositoryPathReference != nil { + return obj.GitRepositoryPathReference + } + + if obj.HelmChartRepositoryChartReference != nil { + return obj.HelmChartRepositoryChartReference + } + + // all schemas are nil + return nil +} + +type NullableHelmChartReference struct { + value *HelmChartReference + isSet bool +} + +func (v NullableHelmChartReference) Get() *HelmChartReference { + return v.value +} + +func (v *NullableHelmChartReference) Set(val *HelmChartReference) { + v.value = val + v.isSet = true +} + +func (v NullableHelmChartReference) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmChartReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmChartReference(val *HelmChartReference) *NullableHelmChartReference { + return &NullableHelmChartReference{value: val, isSet: true} +} + +func (v NullableHelmChartReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmChartReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_chart_repository_chart_reference.go b/pkg/api/model_helm_chart_repository_chart_reference.go new file mode 100644 index 00000000..de0d129d --- /dev/null +++ b/pkg/api/model_helm_chart_repository_chart_reference.go @@ -0,0 +1,251 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmChartRepositoryChartReference type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmChartRepositoryChartReference{} + +// HelmChartRepositoryChartReference struct for HelmChartRepositoryChartReference +type HelmChartRepositoryChartReference struct { + RepositoryType string `json:"repositoryType"` + RepositoryURL string `json:"repositoryURL"` + ChartName string `json:"chartName"` + ChartVersion *string `json:"chartVersion,omitempty"` +} + +type _HelmChartRepositoryChartReference HelmChartRepositoryChartReference + +// NewHelmChartRepositoryChartReference instantiates a new HelmChartRepositoryChartReference object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmChartRepositoryChartReference(repositoryType string, repositoryURL string, chartName string) *HelmChartRepositoryChartReference { + this := HelmChartRepositoryChartReference{} + this.RepositoryType = repositoryType + this.RepositoryURL = repositoryURL + this.ChartName = chartName + return &this +} + +// NewHelmChartRepositoryChartReferenceWithDefaults instantiates a new HelmChartRepositoryChartReference object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmChartRepositoryChartReferenceWithDefaults() *HelmChartRepositoryChartReference { + this := HelmChartRepositoryChartReference{} + return &this +} + +// GetRepositoryType returns the RepositoryType field value +func (o *HelmChartRepositoryChartReference) GetRepositoryType() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryType +} + +// GetRepositoryTypeOk returns a tuple with the RepositoryType field value +// and a boolean to check if the value has been set. +func (o *HelmChartRepositoryChartReference) GetRepositoryTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryType, true +} + +// SetRepositoryType sets field value +func (o *HelmChartRepositoryChartReference) SetRepositoryType(v string) { + o.RepositoryType = v +} + +// GetRepositoryURL returns the RepositoryURL field value +func (o *HelmChartRepositoryChartReference) GetRepositoryURL() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryURL +} + +// GetRepositoryURLOk returns a tuple with the RepositoryURL field value +// and a boolean to check if the value has been set. +func (o *HelmChartRepositoryChartReference) GetRepositoryURLOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryURL, true +} + +// SetRepositoryURL sets field value +func (o *HelmChartRepositoryChartReference) SetRepositoryURL(v string) { + o.RepositoryURL = v +} + +// GetChartName returns the ChartName field value +func (o *HelmChartRepositoryChartReference) GetChartName() string { + if o == nil { + var ret string + return ret + } + + return o.ChartName +} + +// GetChartNameOk returns a tuple with the ChartName field value +// and a boolean to check if the value has been set. +func (o *HelmChartRepositoryChartReference) GetChartNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ChartName, true +} + +// SetChartName sets field value +func (o *HelmChartRepositoryChartReference) SetChartName(v string) { + o.ChartName = v +} + +// GetChartVersion returns the ChartVersion field value if set, zero value otherwise. +func (o *HelmChartRepositoryChartReference) GetChartVersion() string { + if o == nil || IsNil(o.ChartVersion) { + var ret string + return ret + } + return *o.ChartVersion +} + +// GetChartVersionOk returns a tuple with the ChartVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmChartRepositoryChartReference) GetChartVersionOk() (*string, bool) { + if o == nil || IsNil(o.ChartVersion) { + return nil, false + } + return o.ChartVersion, true +} + +// HasChartVersion returns a boolean if a field has been set. +func (o *HelmChartRepositoryChartReference) HasChartVersion() bool { + if o != nil && !IsNil(o.ChartVersion) { + return true + } + + return false +} + +// SetChartVersion gets a reference to the given string and assigns it to the ChartVersion field. +func (o *HelmChartRepositoryChartReference) SetChartVersion(v string) { + o.ChartVersion = &v +} + +func (o HelmChartRepositoryChartReference) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmChartRepositoryChartReference) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["repositoryType"] = o.RepositoryType + toSerialize["repositoryURL"] = o.RepositoryURL + toSerialize["chartName"] = o.ChartName + if !IsNil(o.ChartVersion) { + toSerialize["chartVersion"] = o.ChartVersion + } + return toSerialize, nil +} + +func (o *HelmChartRepositoryChartReference) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "repositoryType", + "repositoryURL", + "chartName", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmChartRepositoryChartReference := _HelmChartRepositoryChartReference{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmChartRepositoryChartReference) + + if err != nil { + return err + } + + *o = HelmChartRepositoryChartReference(varHelmChartRepositoryChartReference) + + return err +} + +type NullableHelmChartRepositoryChartReference struct { + value *HelmChartRepositoryChartReference + isSet bool +} + +func (v NullableHelmChartRepositoryChartReference) Get() *HelmChartRepositoryChartReference { + return v.value +} + +func (v *NullableHelmChartRepositoryChartReference) Set(val *HelmChartRepositoryChartReference) { + v.value = val + v.isSet = true +} + +func (v NullableHelmChartRepositoryChartReference) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmChartRepositoryChartReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmChartRepositoryChartReference(val *HelmChartRepositoryChartReference) *NullableHelmChartRepositoryChartReference { + return &NullableHelmChartRepositoryChartReference{value: val, isSet: true} +} + +func (v NullableHelmChartRepositoryChartReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmChartRepositoryChartReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_chart_repository_reference.go b/pkg/api/model_helm_chart_repository_reference.go new file mode 100644 index 00000000..2c25f909 --- /dev/null +++ b/pkg/api/model_helm_chart_repository_reference.go @@ -0,0 +1,187 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmChartRepositoryReference type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmChartRepositoryReference{} + +// HelmChartRepositoryReference struct for HelmChartRepositoryReference +type HelmChartRepositoryReference struct { + RepositoryType string `json:"repositoryType"` + RepositoryURL string `json:"repositoryURL"` +} + +type _HelmChartRepositoryReference HelmChartRepositoryReference + +// NewHelmChartRepositoryReference instantiates a new HelmChartRepositoryReference object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmChartRepositoryReference(repositoryType string, repositoryURL string) *HelmChartRepositoryReference { + this := HelmChartRepositoryReference{} + this.RepositoryType = repositoryType + this.RepositoryURL = repositoryURL + return &this +} + +// NewHelmChartRepositoryReferenceWithDefaults instantiates a new HelmChartRepositoryReference object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmChartRepositoryReferenceWithDefaults() *HelmChartRepositoryReference { + this := HelmChartRepositoryReference{} + return &this +} + +// GetRepositoryType returns the RepositoryType field value +func (o *HelmChartRepositoryReference) GetRepositoryType() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryType +} + +// GetRepositoryTypeOk returns a tuple with the RepositoryType field value +// and a boolean to check if the value has been set. +func (o *HelmChartRepositoryReference) GetRepositoryTypeOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryType, true +} + +// SetRepositoryType sets field value +func (o *HelmChartRepositoryReference) SetRepositoryType(v string) { + o.RepositoryType = v +} + +// GetRepositoryURL returns the RepositoryURL field value +func (o *HelmChartRepositoryReference) GetRepositoryURL() string { + if o == nil { + var ret string + return ret + } + + return o.RepositoryURL +} + +// GetRepositoryURLOk returns a tuple with the RepositoryURL field value +// and a boolean to check if the value has been set. +func (o *HelmChartRepositoryReference) GetRepositoryURLOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.RepositoryURL, true +} + +// SetRepositoryURL sets field value +func (o *HelmChartRepositoryReference) SetRepositoryURL(v string) { + o.RepositoryURL = v +} + +func (o HelmChartRepositoryReference) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmChartRepositoryReference) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["repositoryType"] = o.RepositoryType + toSerialize["repositoryURL"] = o.RepositoryURL + return toSerialize, nil +} + +func (o *HelmChartRepositoryReference) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "repositoryType", + "repositoryURL", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmChartRepositoryReference := _HelmChartRepositoryReference{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmChartRepositoryReference) + + if err != nil { + return err + } + + *o = HelmChartRepositoryReference(varHelmChartRepositoryReference) + + return err +} + +type NullableHelmChartRepositoryReference struct { + value *HelmChartRepositoryReference + isSet bool +} + +func (v NullableHelmChartRepositoryReference) Get() *HelmChartRepositoryReference { + return v.value +} + +func (v *NullableHelmChartRepositoryReference) Set(val *HelmChartRepositoryReference) { + v.value = val + v.isSet = true +} + +func (v NullableHelmChartRepositoryReference) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmChartRepositoryReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmChartRepositoryReference(val *HelmChartRepositoryReference) *NullableHelmChartRepositoryReference { + return &NullableHelmChartRepositoryReference{value: val, isSet: true} +} + +func (v NullableHelmChartRepositoryReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmChartRepositoryReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_get_chart_metadata_action.go b/pkg/api/model_helm_get_chart_metadata_action.go new file mode 100644 index 00000000..dd8bb7a1 --- /dev/null +++ b/pkg/api/model_helm_get_chart_metadata_action.go @@ -0,0 +1,159 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmGetChartMetadataAction type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmGetChartMetadataAction{} + +// HelmGetChartMetadataAction struct for HelmGetChartMetadataAction +type HelmGetChartMetadataAction struct { + Reference HelmChartReference `json:"reference"` +} + +type _HelmGetChartMetadataAction HelmGetChartMetadataAction + +// NewHelmGetChartMetadataAction instantiates a new HelmGetChartMetadataAction object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmGetChartMetadataAction(reference HelmChartReference) *HelmGetChartMetadataAction { + this := HelmGetChartMetadataAction{} + this.Reference = reference + return &this +} + +// NewHelmGetChartMetadataActionWithDefaults instantiates a new HelmGetChartMetadataAction object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmGetChartMetadataActionWithDefaults() *HelmGetChartMetadataAction { + this := HelmGetChartMetadataAction{} + return &this +} + +// GetReference returns the Reference field value +func (o *HelmGetChartMetadataAction) GetReference() HelmChartReference { + if o == nil { + var ret HelmChartReference + return ret + } + + return o.Reference +} + +// GetReferenceOk returns a tuple with the Reference field value +// and a boolean to check if the value has been set. +func (o *HelmGetChartMetadataAction) GetReferenceOk() (*HelmChartReference, bool) { + if o == nil { + return nil, false + } + return &o.Reference, true +} + +// SetReference sets field value +func (o *HelmGetChartMetadataAction) SetReference(v HelmChartReference) { + o.Reference = v +} + +func (o HelmGetChartMetadataAction) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmGetChartMetadataAction) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["reference"] = o.Reference + return toSerialize, nil +} + +func (o *HelmGetChartMetadataAction) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "reference", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmGetChartMetadataAction := _HelmGetChartMetadataAction{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmGetChartMetadataAction) + + if err != nil { + return err + } + + *o = HelmGetChartMetadataAction(varHelmGetChartMetadataAction) + + return err +} + +type NullableHelmGetChartMetadataAction struct { + value *HelmGetChartMetadataAction + isSet bool +} + +func (v NullableHelmGetChartMetadataAction) Get() *HelmGetChartMetadataAction { + return v.value +} + +func (v *NullableHelmGetChartMetadataAction) Set(val *HelmGetChartMetadataAction) { + v.value = val + v.isSet = true +} + +func (v NullableHelmGetChartMetadataAction) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmGetChartMetadataAction) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmGetChartMetadataAction(val *HelmGetChartMetadataAction) *NullableHelmGetChartMetadataAction { + return &NullableHelmGetChartMetadataAction{value: val, isSet: true} +} + +func (v NullableHelmGetChartMetadataAction) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmGetChartMetadataAction) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_get_chart_metadata_action_response.go b/pkg/api/model_helm_get_chart_metadata_action_response.go new file mode 100644 index 00000000..ef37f9c0 --- /dev/null +++ b/pkg/api/model_helm_get_chart_metadata_action_response.go @@ -0,0 +1,159 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmGetChartMetadataActionResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmGetChartMetadataActionResponse{} + +// HelmGetChartMetadataActionResponse struct for HelmGetChartMetadataActionResponse +type HelmGetChartMetadataActionResponse struct { + DefaultValues map[string]interface{} `json:"defaultValues"` +} + +type _HelmGetChartMetadataActionResponse HelmGetChartMetadataActionResponse + +// NewHelmGetChartMetadataActionResponse instantiates a new HelmGetChartMetadataActionResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmGetChartMetadataActionResponse(defaultValues map[string]interface{}) *HelmGetChartMetadataActionResponse { + this := HelmGetChartMetadataActionResponse{} + this.DefaultValues = defaultValues + return &this +} + +// NewHelmGetChartMetadataActionResponseWithDefaults instantiates a new HelmGetChartMetadataActionResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmGetChartMetadataActionResponseWithDefaults() *HelmGetChartMetadataActionResponse { + this := HelmGetChartMetadataActionResponse{} + return &this +} + +// GetDefaultValues returns the DefaultValues field value +func (o *HelmGetChartMetadataActionResponse) GetDefaultValues() map[string]interface{} { + if o == nil { + var ret map[string]interface{} + return ret + } + + return o.DefaultValues +} + +// GetDefaultValuesOk returns a tuple with the DefaultValues field value +// and a boolean to check if the value has been set. +func (o *HelmGetChartMetadataActionResponse) GetDefaultValuesOk() (map[string]interface{}, bool) { + if o == nil { + return map[string]interface{}{}, false + } + return o.DefaultValues, true +} + +// SetDefaultValues sets field value +func (o *HelmGetChartMetadataActionResponse) SetDefaultValues(v map[string]interface{}) { + o.DefaultValues = v +} + +func (o HelmGetChartMetadataActionResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmGetChartMetadataActionResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["defaultValues"] = o.DefaultValues + return toSerialize, nil +} + +func (o *HelmGetChartMetadataActionResponse) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "defaultValues", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmGetChartMetadataActionResponse := _HelmGetChartMetadataActionResponse{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmGetChartMetadataActionResponse) + + if err != nil { + return err + } + + *o = HelmGetChartMetadataActionResponse(varHelmGetChartMetadataActionResponse) + + return err +} + +type NullableHelmGetChartMetadataActionResponse struct { + value *HelmGetChartMetadataActionResponse + isSet bool +} + +func (v NullableHelmGetChartMetadataActionResponse) Get() *HelmGetChartMetadataActionResponse { + return v.value +} + +func (v *NullableHelmGetChartMetadataActionResponse) Set(val *HelmGetChartMetadataActionResponse) { + v.value = val + v.isSet = true +} + +func (v NullableHelmGetChartMetadataActionResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmGetChartMetadataActionResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmGetChartMetadataActionResponse(val *HelmGetChartMetadataActionResponse) *NullableHelmGetChartMetadataActionResponse { + return &NullableHelmGetChartMetadataActionResponse{value: val, isSet: true} +} + +func (v NullableHelmGetChartMetadataActionResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmGetChartMetadataActionResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_list_charts_action.go b/pkg/api/model_helm_list_charts_action.go new file mode 100644 index 00000000..8db14a2b --- /dev/null +++ b/pkg/api/model_helm_list_charts_action.go @@ -0,0 +1,159 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmListChartsAction type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmListChartsAction{} + +// HelmListChartsAction struct for HelmListChartsAction +type HelmListChartsAction struct { + Reference HelmRepositoryReference `json:"reference"` +} + +type _HelmListChartsAction HelmListChartsAction + +// NewHelmListChartsAction instantiates a new HelmListChartsAction object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmListChartsAction(reference HelmRepositoryReference) *HelmListChartsAction { + this := HelmListChartsAction{} + this.Reference = reference + return &this +} + +// NewHelmListChartsActionWithDefaults instantiates a new HelmListChartsAction object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmListChartsActionWithDefaults() *HelmListChartsAction { + this := HelmListChartsAction{} + return &this +} + +// GetReference returns the Reference field value +func (o *HelmListChartsAction) GetReference() HelmRepositoryReference { + if o == nil { + var ret HelmRepositoryReference + return ret + } + + return o.Reference +} + +// GetReferenceOk returns a tuple with the Reference field value +// and a boolean to check if the value has been set. +func (o *HelmListChartsAction) GetReferenceOk() (*HelmRepositoryReference, bool) { + if o == nil { + return nil, false + } + return &o.Reference, true +} + +// SetReference sets field value +func (o *HelmListChartsAction) SetReference(v HelmRepositoryReference) { + o.Reference = v +} + +func (o HelmListChartsAction) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmListChartsAction) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["reference"] = o.Reference + return toSerialize, nil +} + +func (o *HelmListChartsAction) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "reference", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmListChartsAction := _HelmListChartsAction{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmListChartsAction) + + if err != nil { + return err + } + + *o = HelmListChartsAction(varHelmListChartsAction) + + return err +} + +type NullableHelmListChartsAction struct { + value *HelmListChartsAction + isSet bool +} + +func (v NullableHelmListChartsAction) Get() *HelmListChartsAction { + return v.value +} + +func (v *NullableHelmListChartsAction) Set(val *HelmListChartsAction) { + v.value = val + v.isSet = true +} + +func (v NullableHelmListChartsAction) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmListChartsAction) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmListChartsAction(val *HelmListChartsAction) *NullableHelmListChartsAction { + return &NullableHelmListChartsAction{value: val, isSet: true} +} + +func (v NullableHelmListChartsAction) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmListChartsAction) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_list_charts_action_response.go b/pkg/api/model_helm_list_charts_action_response.go new file mode 100644 index 00000000..0a6d2ad6 --- /dev/null +++ b/pkg/api/model_helm_list_charts_action_response.go @@ -0,0 +1,159 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmListChartsActionResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmListChartsActionResponse{} + +// HelmListChartsActionResponse struct for HelmListChartsActionResponse +type HelmListChartsActionResponse struct { + Items []map[string]interface{} `json:"items"` +} + +type _HelmListChartsActionResponse HelmListChartsActionResponse + +// NewHelmListChartsActionResponse instantiates a new HelmListChartsActionResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmListChartsActionResponse(items []map[string]interface{}) *HelmListChartsActionResponse { + this := HelmListChartsActionResponse{} + this.Items = items + return &this +} + +// NewHelmListChartsActionResponseWithDefaults instantiates a new HelmListChartsActionResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmListChartsActionResponseWithDefaults() *HelmListChartsActionResponse { + this := HelmListChartsActionResponse{} + return &this +} + +// GetItems returns the Items field value +func (o *HelmListChartsActionResponse) GetItems() []map[string]interface{} { + if o == nil { + var ret []map[string]interface{} + return ret + } + + return o.Items +} + +// GetItemsOk returns a tuple with the Items field value +// and a boolean to check if the value has been set. +func (o *HelmListChartsActionResponse) GetItemsOk() ([]map[string]interface{}, bool) { + if o == nil { + return nil, false + } + return o.Items, true +} + +// SetItems sets field value +func (o *HelmListChartsActionResponse) SetItems(v []map[string]interface{}) { + o.Items = v +} + +func (o HelmListChartsActionResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmListChartsActionResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["items"] = o.Items + return toSerialize, nil +} + +func (o *HelmListChartsActionResponse) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "items", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmListChartsActionResponse := _HelmListChartsActionResponse{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmListChartsActionResponse) + + if err != nil { + return err + } + + *o = HelmListChartsActionResponse(varHelmListChartsActionResponse) + + return err +} + +type NullableHelmListChartsActionResponse struct { + value *HelmListChartsActionResponse + isSet bool +} + +func (v NullableHelmListChartsActionResponse) Get() *HelmListChartsActionResponse { + return v.value +} + +func (v *NullableHelmListChartsActionResponse) Set(val *HelmListChartsActionResponse) { + v.value = val + v.isSet = true +} + +func (v NullableHelmListChartsActionResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmListChartsActionResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmListChartsActionResponse(val *HelmListChartsActionResponse) *NullableHelmListChartsActionResponse { + return &NullableHelmListChartsActionResponse{value: val, isSet: true} +} + +func (v NullableHelmListChartsActionResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmListChartsActionResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_render_chart_action.go b/pkg/api/model_helm_render_chart_action.go new file mode 100644 index 00000000..5c5dff4b --- /dev/null +++ b/pkg/api/model_helm_render_chart_action.go @@ -0,0 +1,195 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmRenderChartAction type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmRenderChartAction{} + +// HelmRenderChartAction struct for HelmRenderChartAction +type HelmRenderChartAction struct { + Reference HelmChartReference `json:"reference"` + Parameters *HelmRenderParameters `json:"parameters,omitempty"` +} + +type _HelmRenderChartAction HelmRenderChartAction + +// NewHelmRenderChartAction instantiates a new HelmRenderChartAction object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmRenderChartAction(reference HelmChartReference) *HelmRenderChartAction { + this := HelmRenderChartAction{} + this.Reference = reference + return &this +} + +// NewHelmRenderChartActionWithDefaults instantiates a new HelmRenderChartAction object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmRenderChartActionWithDefaults() *HelmRenderChartAction { + this := HelmRenderChartAction{} + return &this +} + +// GetReference returns the Reference field value +func (o *HelmRenderChartAction) GetReference() HelmChartReference { + if o == nil { + var ret HelmChartReference + return ret + } + + return o.Reference +} + +// GetReferenceOk returns a tuple with the Reference field value +// and a boolean to check if the value has been set. +func (o *HelmRenderChartAction) GetReferenceOk() (*HelmChartReference, bool) { + if o == nil { + return nil, false + } + return &o.Reference, true +} + +// SetReference sets field value +func (o *HelmRenderChartAction) SetReference(v HelmChartReference) { + o.Reference = v +} + +// GetParameters returns the Parameters field value if set, zero value otherwise. +func (o *HelmRenderChartAction) GetParameters() HelmRenderParameters { + if o == nil || IsNil(o.Parameters) { + var ret HelmRenderParameters + return ret + } + return *o.Parameters +} + +// GetParametersOk returns a tuple with the Parameters field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderChartAction) GetParametersOk() (*HelmRenderParameters, bool) { + if o == nil || IsNil(o.Parameters) { + return nil, false + } + return o.Parameters, true +} + +// HasParameters returns a boolean if a field has been set. +func (o *HelmRenderChartAction) HasParameters() bool { + if o != nil && !IsNil(o.Parameters) { + return true + } + + return false +} + +// SetParameters gets a reference to the given HelmRenderParameters and assigns it to the Parameters field. +func (o *HelmRenderChartAction) SetParameters(v HelmRenderParameters) { + o.Parameters = &v +} + +func (o HelmRenderChartAction) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmRenderChartAction) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["reference"] = o.Reference + if !IsNil(o.Parameters) { + toSerialize["parameters"] = o.Parameters + } + return toSerialize, nil +} + +func (o *HelmRenderChartAction) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "reference", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmRenderChartAction := _HelmRenderChartAction{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmRenderChartAction) + + if err != nil { + return err + } + + *o = HelmRenderChartAction(varHelmRenderChartAction) + + return err +} + +type NullableHelmRenderChartAction struct { + value *HelmRenderChartAction + isSet bool +} + +func (v NullableHelmRenderChartAction) Get() *HelmRenderChartAction { + return v.value +} + +func (v *NullableHelmRenderChartAction) Set(val *HelmRenderChartAction) { + v.value = val + v.isSet = true +} + +func (v NullableHelmRenderChartAction) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmRenderChartAction) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmRenderChartAction(val *HelmRenderChartAction) *NullableHelmRenderChartAction { + return &NullableHelmRenderChartAction{value: val, isSet: true} +} + +func (v NullableHelmRenderChartAction) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmRenderChartAction) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_render_chart_action_response.go b/pkg/api/model_helm_render_chart_action_response.go new file mode 100644 index 00000000..702618f2 --- /dev/null +++ b/pkg/api/model_helm_render_chart_action_response.go @@ -0,0 +1,163 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// checks if the HelmRenderChartActionResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmRenderChartActionResponse{} + +// HelmRenderChartActionResponse struct for HelmRenderChartActionResponse +type HelmRenderChartActionResponse struct { + Manifests []Manifest `json:"manifests,omitempty"` + Metadata *HelmRenderMetadata `json:"metadata,omitempty"` +} + +// NewHelmRenderChartActionResponse instantiates a new HelmRenderChartActionResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmRenderChartActionResponse() *HelmRenderChartActionResponse { + this := HelmRenderChartActionResponse{} + return &this +} + +// NewHelmRenderChartActionResponseWithDefaults instantiates a new HelmRenderChartActionResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmRenderChartActionResponseWithDefaults() *HelmRenderChartActionResponse { + this := HelmRenderChartActionResponse{} + return &this +} + +// GetManifests returns the Manifests field value if set, zero value otherwise. +func (o *HelmRenderChartActionResponse) GetManifests() []Manifest { + if o == nil || IsNil(o.Manifests) { + var ret []Manifest + return ret + } + return o.Manifests +} + +// GetManifestsOk returns a tuple with the Manifests field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderChartActionResponse) GetManifestsOk() ([]Manifest, bool) { + if o == nil || IsNil(o.Manifests) { + return nil, false + } + return o.Manifests, true +} + +// HasManifests returns a boolean if a field has been set. +func (o *HelmRenderChartActionResponse) HasManifests() bool { + if o != nil && !IsNil(o.Manifests) { + return true + } + + return false +} + +// SetManifests gets a reference to the given []Manifest and assigns it to the Manifests field. +func (o *HelmRenderChartActionResponse) SetManifests(v []Manifest) { + o.Manifests = v +} + +// GetMetadata returns the Metadata field value if set, zero value otherwise. +func (o *HelmRenderChartActionResponse) GetMetadata() HelmRenderMetadata { + if o == nil || IsNil(o.Metadata) { + var ret HelmRenderMetadata + return ret + } + return *o.Metadata +} + +// GetMetadataOk returns a tuple with the Metadata field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderChartActionResponse) GetMetadataOk() (*HelmRenderMetadata, bool) { + if o == nil || IsNil(o.Metadata) { + return nil, false + } + return o.Metadata, true +} + +// HasMetadata returns a boolean if a field has been set. +func (o *HelmRenderChartActionResponse) HasMetadata() bool { + if o != nil && !IsNil(o.Metadata) { + return true + } + + return false +} + +// SetMetadata gets a reference to the given HelmRenderMetadata and assigns it to the Metadata field. +func (o *HelmRenderChartActionResponse) SetMetadata(v HelmRenderMetadata) { + o.Metadata = &v +} + +func (o HelmRenderChartActionResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmRenderChartActionResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Manifests) { + toSerialize["manifests"] = o.Manifests + } + if !IsNil(o.Metadata) { + toSerialize["metadata"] = o.Metadata + } + return toSerialize, nil +} + +type NullableHelmRenderChartActionResponse struct { + value *HelmRenderChartActionResponse + isSet bool +} + +func (v NullableHelmRenderChartActionResponse) Get() *HelmRenderChartActionResponse { + return v.value +} + +func (v *NullableHelmRenderChartActionResponse) Set(val *HelmRenderChartActionResponse) { + v.value = val + v.isSet = true +} + +func (v NullableHelmRenderChartActionResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmRenderChartActionResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmRenderChartActionResponse(val *HelmRenderChartActionResponse) *NullableHelmRenderChartActionResponse { + return &NullableHelmRenderChartActionResponse{value: val, isSet: true} +} + +func (v NullableHelmRenderChartActionResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmRenderChartActionResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_render_metadata.go b/pkg/api/model_helm_render_metadata.go new file mode 100644 index 00000000..c4e8a59f --- /dev/null +++ b/pkg/api/model_helm_render_metadata.go @@ -0,0 +1,299 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the HelmRenderMetadata type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmRenderMetadata{} + +// HelmRenderMetadata struct for HelmRenderMetadata +type HelmRenderMetadata struct { + ReleaseName string `json:"releaseName"` + Namespace string `json:"namespace"` + ApiVersions []string `json:"apiVersions"` + KubeVersion string `json:"kubeVersion"` + HelmVersion string `json:"helmVersion"` + MergedValues map[string]interface{} `json:"mergedValues"` +} + +type _HelmRenderMetadata HelmRenderMetadata + +// NewHelmRenderMetadata instantiates a new HelmRenderMetadata object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmRenderMetadata(releaseName string, namespace string, apiVersions []string, kubeVersion string, helmVersion string, mergedValues map[string]interface{}) *HelmRenderMetadata { + this := HelmRenderMetadata{} + this.ReleaseName = releaseName + this.Namespace = namespace + this.ApiVersions = apiVersions + this.KubeVersion = kubeVersion + this.HelmVersion = helmVersion + this.MergedValues = mergedValues + return &this +} + +// NewHelmRenderMetadataWithDefaults instantiates a new HelmRenderMetadata object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmRenderMetadataWithDefaults() *HelmRenderMetadata { + this := HelmRenderMetadata{} + return &this +} + +// GetReleaseName returns the ReleaseName field value +func (o *HelmRenderMetadata) GetReleaseName() string { + if o == nil { + var ret string + return ret + } + + return o.ReleaseName +} + +// GetReleaseNameOk returns a tuple with the ReleaseName field value +// and a boolean to check if the value has been set. +func (o *HelmRenderMetadata) GetReleaseNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.ReleaseName, true +} + +// SetReleaseName sets field value +func (o *HelmRenderMetadata) SetReleaseName(v string) { + o.ReleaseName = v +} + +// GetNamespace returns the Namespace field value +func (o *HelmRenderMetadata) GetNamespace() string { + if o == nil { + var ret string + return ret + } + + return o.Namespace +} + +// GetNamespaceOk returns a tuple with the Namespace field value +// and a boolean to check if the value has been set. +func (o *HelmRenderMetadata) GetNamespaceOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.Namespace, true +} + +// SetNamespace sets field value +func (o *HelmRenderMetadata) SetNamespace(v string) { + o.Namespace = v +} + +// GetApiVersions returns the ApiVersions field value +func (o *HelmRenderMetadata) GetApiVersions() []string { + if o == nil { + var ret []string + return ret + } + + return o.ApiVersions +} + +// GetApiVersionsOk returns a tuple with the ApiVersions field value +// and a boolean to check if the value has been set. +func (o *HelmRenderMetadata) GetApiVersionsOk() ([]string, bool) { + if o == nil { + return nil, false + } + return o.ApiVersions, true +} + +// SetApiVersions sets field value +func (o *HelmRenderMetadata) SetApiVersions(v []string) { + o.ApiVersions = v +} + +// GetKubeVersion returns the KubeVersion field value +func (o *HelmRenderMetadata) GetKubeVersion() string { + if o == nil { + var ret string + return ret + } + + return o.KubeVersion +} + +// GetKubeVersionOk returns a tuple with the KubeVersion field value +// and a boolean to check if the value has been set. +func (o *HelmRenderMetadata) GetKubeVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.KubeVersion, true +} + +// SetKubeVersion sets field value +func (o *HelmRenderMetadata) SetKubeVersion(v string) { + o.KubeVersion = v +} + +// GetHelmVersion returns the HelmVersion field value +func (o *HelmRenderMetadata) GetHelmVersion() string { + if o == nil { + var ret string + return ret + } + + return o.HelmVersion +} + +// GetHelmVersionOk returns a tuple with the HelmVersion field value +// and a boolean to check if the value has been set. +func (o *HelmRenderMetadata) GetHelmVersionOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.HelmVersion, true +} + +// SetHelmVersion sets field value +func (o *HelmRenderMetadata) SetHelmVersion(v string) { + o.HelmVersion = v +} + +// GetMergedValues returns the MergedValues field value +func (o *HelmRenderMetadata) GetMergedValues() map[string]interface{} { + if o == nil { + var ret map[string]interface{} + return ret + } + + return o.MergedValues +} + +// GetMergedValuesOk returns a tuple with the MergedValues field value +// and a boolean to check if the value has been set. +func (o *HelmRenderMetadata) GetMergedValuesOk() (map[string]interface{}, bool) { + if o == nil { + return map[string]interface{}{}, false + } + return o.MergedValues, true +} + +// SetMergedValues sets field value +func (o *HelmRenderMetadata) SetMergedValues(v map[string]interface{}) { + o.MergedValues = v +} + +func (o HelmRenderMetadata) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmRenderMetadata) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["releaseName"] = o.ReleaseName + toSerialize["namespace"] = o.Namespace + toSerialize["apiVersions"] = o.ApiVersions + toSerialize["kubeVersion"] = o.KubeVersion + toSerialize["helmVersion"] = o.HelmVersion + toSerialize["mergedValues"] = o.MergedValues + return toSerialize, nil +} + +func (o *HelmRenderMetadata) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "releaseName", + "namespace", + "apiVersions", + "kubeVersion", + "helmVersion", + "mergedValues", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varHelmRenderMetadata := _HelmRenderMetadata{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varHelmRenderMetadata) + + if err != nil { + return err + } + + *o = HelmRenderMetadata(varHelmRenderMetadata) + + return err +} + +type NullableHelmRenderMetadata struct { + value *HelmRenderMetadata + isSet bool +} + +func (v NullableHelmRenderMetadata) Get() *HelmRenderMetadata { + return v.value +} + +func (v *NullableHelmRenderMetadata) Set(val *HelmRenderMetadata) { + v.value = val + v.isSet = true +} + +func (v NullableHelmRenderMetadata) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmRenderMetadata) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmRenderMetadata(val *HelmRenderMetadata) *NullableHelmRenderMetadata { + return &NullableHelmRenderMetadata{value: val, isSet: true} +} + +func (v NullableHelmRenderMetadata) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmRenderMetadata) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_render_parameters.go b/pkg/api/model_helm_render_parameters.go new file mode 100644 index 00000000..09e8ade5 --- /dev/null +++ b/pkg/api/model_helm_render_parameters.go @@ -0,0 +1,573 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// checks if the HelmRenderParameters type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &HelmRenderParameters{} + +// HelmRenderParameters struct for HelmRenderParameters +type HelmRenderParameters struct { + ReleaseName *string `json:"releaseName,omitempty"` + Namespace *string `json:"namespace,omitempty"` + KubeVersion *string `json:"kubeVersion,omitempty"` + ApiVersions []string `json:"apiVersions,omitempty"` + ValueFiles []string `json:"valueFiles,omitempty"` + Values map[string]string `json:"values,omitempty"` + ValuesFlat []string `json:"valuesFlat,omitempty"` + StringValues map[string]string `json:"stringValues,omitempty"` + StringValuesFlat []string `json:"stringValuesFlat,omitempty"` + ComplexValues map[string]interface{} `json:"complexValues,omitempty"` + IncludeCRDs *bool `json:"includeCRDs,omitempty"` + IncludeHooks *bool `json:"includeHooks,omitempty"` + IgnoreMissingValueFiles *bool `json:"ignoreMissingValueFiles,omitempty"` +} + +// NewHelmRenderParameters instantiates a new HelmRenderParameters object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewHelmRenderParameters() *HelmRenderParameters { + this := HelmRenderParameters{} + var includeCRDs bool = true + this.IncludeCRDs = &includeCRDs + var includeHooks bool = true + this.IncludeHooks = &includeHooks + var ignoreMissingValueFiles bool = false + this.IgnoreMissingValueFiles = &ignoreMissingValueFiles + return &this +} + +// NewHelmRenderParametersWithDefaults instantiates a new HelmRenderParameters object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewHelmRenderParametersWithDefaults() *HelmRenderParameters { + this := HelmRenderParameters{} + var includeCRDs bool = true + this.IncludeCRDs = &includeCRDs + var includeHooks bool = true + this.IncludeHooks = &includeHooks + var ignoreMissingValueFiles bool = false + this.IgnoreMissingValueFiles = &ignoreMissingValueFiles + return &this +} + +// GetReleaseName returns the ReleaseName field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetReleaseName() string { + if o == nil || IsNil(o.ReleaseName) { + var ret string + return ret + } + return *o.ReleaseName +} + +// GetReleaseNameOk returns a tuple with the ReleaseName field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetReleaseNameOk() (*string, bool) { + if o == nil || IsNil(o.ReleaseName) { + return nil, false + } + return o.ReleaseName, true +} + +// HasReleaseName returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasReleaseName() bool { + if o != nil && !IsNil(o.ReleaseName) { + return true + } + + return false +} + +// SetReleaseName gets a reference to the given string and assigns it to the ReleaseName field. +func (o *HelmRenderParameters) SetReleaseName(v string) { + o.ReleaseName = &v +} + +// GetNamespace returns the Namespace field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetNamespace() string { + if o == nil || IsNil(o.Namespace) { + var ret string + return ret + } + return *o.Namespace +} + +// GetNamespaceOk returns a tuple with the Namespace field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetNamespaceOk() (*string, bool) { + if o == nil || IsNil(o.Namespace) { + return nil, false + } + return o.Namespace, true +} + +// HasNamespace returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasNamespace() bool { + if o != nil && !IsNil(o.Namespace) { + return true + } + + return false +} + +// SetNamespace gets a reference to the given string and assigns it to the Namespace field. +func (o *HelmRenderParameters) SetNamespace(v string) { + o.Namespace = &v +} + +// GetKubeVersion returns the KubeVersion field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetKubeVersion() string { + if o == nil || IsNil(o.KubeVersion) { + var ret string + return ret + } + return *o.KubeVersion +} + +// GetKubeVersionOk returns a tuple with the KubeVersion field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetKubeVersionOk() (*string, bool) { + if o == nil || IsNil(o.KubeVersion) { + return nil, false + } + return o.KubeVersion, true +} + +// HasKubeVersion returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasKubeVersion() bool { + if o != nil && !IsNil(o.KubeVersion) { + return true + } + + return false +} + +// SetKubeVersion gets a reference to the given string and assigns it to the KubeVersion field. +func (o *HelmRenderParameters) SetKubeVersion(v string) { + o.KubeVersion = &v +} + +// GetApiVersions returns the ApiVersions field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetApiVersions() []string { + if o == nil || IsNil(o.ApiVersions) { + var ret []string + return ret + } + return o.ApiVersions +} + +// GetApiVersionsOk returns a tuple with the ApiVersions field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetApiVersionsOk() ([]string, bool) { + if o == nil || IsNil(o.ApiVersions) { + return nil, false + } + return o.ApiVersions, true +} + +// HasApiVersions returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasApiVersions() bool { + if o != nil && !IsNil(o.ApiVersions) { + return true + } + + return false +} + +// SetApiVersions gets a reference to the given []string and assigns it to the ApiVersions field. +func (o *HelmRenderParameters) SetApiVersions(v []string) { + o.ApiVersions = v +} + +// GetValueFiles returns the ValueFiles field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetValueFiles() []string { + if o == nil || IsNil(o.ValueFiles) { + var ret []string + return ret + } + return o.ValueFiles +} + +// GetValueFilesOk returns a tuple with the ValueFiles field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetValueFilesOk() ([]string, bool) { + if o == nil || IsNil(o.ValueFiles) { + return nil, false + } + return o.ValueFiles, true +} + +// HasValueFiles returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasValueFiles() bool { + if o != nil && !IsNil(o.ValueFiles) { + return true + } + + return false +} + +// SetValueFiles gets a reference to the given []string and assigns it to the ValueFiles field. +func (o *HelmRenderParameters) SetValueFiles(v []string) { + o.ValueFiles = v +} + +// GetValues returns the Values field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *HelmRenderParameters) GetValues() map[string]string { + if o == nil { + var ret map[string]string + return ret + } + return o.Values +} + +// GetValuesOk returns a tuple with the Values field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *HelmRenderParameters) GetValuesOk() (*map[string]string, bool) { + if o == nil || IsNil(o.Values) { + return nil, false + } + return &o.Values, true +} + +// HasValues returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasValues() bool { + if o != nil && !IsNil(o.Values) { + return true + } + + return false +} + +// SetValues gets a reference to the given map[string]string and assigns it to the Values field. +func (o *HelmRenderParameters) SetValues(v map[string]string) { + o.Values = v +} + +// GetValuesFlat returns the ValuesFlat field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetValuesFlat() []string { + if o == nil || IsNil(o.ValuesFlat) { + var ret []string + return ret + } + return o.ValuesFlat +} + +// GetValuesFlatOk returns a tuple with the ValuesFlat field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetValuesFlatOk() ([]string, bool) { + if o == nil || IsNil(o.ValuesFlat) { + return nil, false + } + return o.ValuesFlat, true +} + +// HasValuesFlat returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasValuesFlat() bool { + if o != nil && !IsNil(o.ValuesFlat) { + return true + } + + return false +} + +// SetValuesFlat gets a reference to the given []string and assigns it to the ValuesFlat field. +func (o *HelmRenderParameters) SetValuesFlat(v []string) { + o.ValuesFlat = v +} + +// GetStringValues returns the StringValues field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *HelmRenderParameters) GetStringValues() map[string]string { + if o == nil { + var ret map[string]string + return ret + } + return o.StringValues +} + +// GetStringValuesOk returns a tuple with the StringValues field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *HelmRenderParameters) GetStringValuesOk() (*map[string]string, bool) { + if o == nil || IsNil(o.StringValues) { + return nil, false + } + return &o.StringValues, true +} + +// HasStringValues returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasStringValues() bool { + if o != nil && !IsNil(o.StringValues) { + return true + } + + return false +} + +// SetStringValues gets a reference to the given map[string]string and assigns it to the StringValues field. +func (o *HelmRenderParameters) SetStringValues(v map[string]string) { + o.StringValues = v +} + +// GetStringValuesFlat returns the StringValuesFlat field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetStringValuesFlat() []string { + if o == nil || IsNil(o.StringValuesFlat) { + var ret []string + return ret + } + return o.StringValuesFlat +} + +// GetStringValuesFlatOk returns a tuple with the StringValuesFlat field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetStringValuesFlatOk() ([]string, bool) { + if o == nil || IsNil(o.StringValuesFlat) { + return nil, false + } + return o.StringValuesFlat, true +} + +// HasStringValuesFlat returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasStringValuesFlat() bool { + if o != nil && !IsNil(o.StringValuesFlat) { + return true + } + + return false +} + +// SetStringValuesFlat gets a reference to the given []string and assigns it to the StringValuesFlat field. +func (o *HelmRenderParameters) SetStringValuesFlat(v []string) { + o.StringValuesFlat = v +} + +// GetComplexValues returns the ComplexValues field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetComplexValues() map[string]interface{} { + if o == nil || IsNil(o.ComplexValues) { + var ret map[string]interface{} + return ret + } + return o.ComplexValues +} + +// GetComplexValuesOk returns a tuple with the ComplexValues field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetComplexValuesOk() (map[string]interface{}, bool) { + if o == nil || IsNil(o.ComplexValues) { + return map[string]interface{}{}, false + } + return o.ComplexValues, true +} + +// HasComplexValues returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasComplexValues() bool { + if o != nil && !IsNil(o.ComplexValues) { + return true + } + + return false +} + +// SetComplexValues gets a reference to the given map[string]interface{} and assigns it to the ComplexValues field. +func (o *HelmRenderParameters) SetComplexValues(v map[string]interface{}) { + o.ComplexValues = v +} + +// GetIncludeCRDs returns the IncludeCRDs field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetIncludeCRDs() bool { + if o == nil || IsNil(o.IncludeCRDs) { + var ret bool + return ret + } + return *o.IncludeCRDs +} + +// GetIncludeCRDsOk returns a tuple with the IncludeCRDs field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetIncludeCRDsOk() (*bool, bool) { + if o == nil || IsNil(o.IncludeCRDs) { + return nil, false + } + return o.IncludeCRDs, true +} + +// HasIncludeCRDs returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasIncludeCRDs() bool { + if o != nil && !IsNil(o.IncludeCRDs) { + return true + } + + return false +} + +// SetIncludeCRDs gets a reference to the given bool and assigns it to the IncludeCRDs field. +func (o *HelmRenderParameters) SetIncludeCRDs(v bool) { + o.IncludeCRDs = &v +} + +// GetIncludeHooks returns the IncludeHooks field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetIncludeHooks() bool { + if o == nil || IsNil(o.IncludeHooks) { + var ret bool + return ret + } + return *o.IncludeHooks +} + +// GetIncludeHooksOk returns a tuple with the IncludeHooks field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetIncludeHooksOk() (*bool, bool) { + if o == nil || IsNil(o.IncludeHooks) { + return nil, false + } + return o.IncludeHooks, true +} + +// HasIncludeHooks returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasIncludeHooks() bool { + if o != nil && !IsNil(o.IncludeHooks) { + return true + } + + return false +} + +// SetIncludeHooks gets a reference to the given bool and assigns it to the IncludeHooks field. +func (o *HelmRenderParameters) SetIncludeHooks(v bool) { + o.IncludeHooks = &v +} + +// GetIgnoreMissingValueFiles returns the IgnoreMissingValueFiles field value if set, zero value otherwise. +func (o *HelmRenderParameters) GetIgnoreMissingValueFiles() bool { + if o == nil || IsNil(o.IgnoreMissingValueFiles) { + var ret bool + return ret + } + return *o.IgnoreMissingValueFiles +} + +// GetIgnoreMissingValueFilesOk returns a tuple with the IgnoreMissingValueFiles field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *HelmRenderParameters) GetIgnoreMissingValueFilesOk() (*bool, bool) { + if o == nil || IsNil(o.IgnoreMissingValueFiles) { + return nil, false + } + return o.IgnoreMissingValueFiles, true +} + +// HasIgnoreMissingValueFiles returns a boolean if a field has been set. +func (o *HelmRenderParameters) HasIgnoreMissingValueFiles() bool { + if o != nil && !IsNil(o.IgnoreMissingValueFiles) { + return true + } + + return false +} + +// SetIgnoreMissingValueFiles gets a reference to the given bool and assigns it to the IgnoreMissingValueFiles field. +func (o *HelmRenderParameters) SetIgnoreMissingValueFiles(v bool) { + o.IgnoreMissingValueFiles = &v +} + +func (o HelmRenderParameters) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o HelmRenderParameters) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.ReleaseName) { + toSerialize["releaseName"] = o.ReleaseName + } + if !IsNil(o.Namespace) { + toSerialize["namespace"] = o.Namespace + } + if !IsNil(o.KubeVersion) { + toSerialize["kubeVersion"] = o.KubeVersion + } + if !IsNil(o.ApiVersions) { + toSerialize["apiVersions"] = o.ApiVersions + } + if !IsNil(o.ValueFiles) { + toSerialize["valueFiles"] = o.ValueFiles + } + if o.Values != nil { + toSerialize["values"] = o.Values + } + if !IsNil(o.ValuesFlat) { + toSerialize["valuesFlat"] = o.ValuesFlat + } + if o.StringValues != nil { + toSerialize["stringValues"] = o.StringValues + } + if !IsNil(o.StringValuesFlat) { + toSerialize["stringValuesFlat"] = o.StringValuesFlat + } + if !IsNil(o.ComplexValues) { + toSerialize["complexValues"] = o.ComplexValues + } + if !IsNil(o.IncludeCRDs) { + toSerialize["includeCRDs"] = o.IncludeCRDs + } + if !IsNil(o.IncludeHooks) { + toSerialize["includeHooks"] = o.IncludeHooks + } + if !IsNil(o.IgnoreMissingValueFiles) { + toSerialize["ignoreMissingValueFiles"] = o.IgnoreMissingValueFiles + } + return toSerialize, nil +} + +type NullableHelmRenderParameters struct { + value *HelmRenderParameters + isSet bool +} + +func (v NullableHelmRenderParameters) Get() *HelmRenderParameters { + return v.value +} + +func (v *NullableHelmRenderParameters) Set(val *HelmRenderParameters) { + v.value = val + v.isSet = true +} + +func (v NullableHelmRenderParameters) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmRenderParameters) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmRenderParameters(val *HelmRenderParameters) *NullableHelmRenderParameters { + return &NullableHelmRenderParameters{value: val, isSet: true} +} + +func (v NullableHelmRenderParameters) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmRenderParameters) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_helm_repository_reference.go b/pkg/api/model_helm_repository_reference.go new file mode 100644 index 00000000..89de8295 --- /dev/null +++ b/pkg/api/model_helm_repository_reference.go @@ -0,0 +1,158 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "gopkg.in/validator.v2" + "fmt" +) + +// HelmRepositoryReference - struct for HelmRepositoryReference +type HelmRepositoryReference struct { + GitRepositoryReference *GitRepositoryReference + HelmChartRepositoryReference *HelmChartRepositoryReference +} + +// GitRepositoryReferenceAsHelmRepositoryReference is a convenience function that returns GitRepositoryReference wrapped in HelmRepositoryReference +func GitRepositoryReferenceAsHelmRepositoryReference(v *GitRepositoryReference) HelmRepositoryReference { + return HelmRepositoryReference{ + GitRepositoryReference: v, + } +} + +// HelmChartRepositoryReferenceAsHelmRepositoryReference is a convenience function that returns HelmChartRepositoryReference wrapped in HelmRepositoryReference +func HelmChartRepositoryReferenceAsHelmRepositoryReference(v *HelmChartRepositoryReference) HelmRepositoryReference { + return HelmRepositoryReference{ + HelmChartRepositoryReference: v, + } +} + + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *HelmRepositoryReference) UnmarshalJSON(data []byte) error { + var err error + match := 0 + // try to unmarshal data into GitRepositoryReference + err = newStrictDecoder(data).Decode(&dst.GitRepositoryReference) + if err == nil { + jsonGitRepositoryReference, _ := json.Marshal(dst.GitRepositoryReference) + if string(jsonGitRepositoryReference) == "{}" { // empty struct + dst.GitRepositoryReference = nil + } else { + if err = validator.Validate(dst.GitRepositoryReference); err != nil { + dst.GitRepositoryReference = nil + } else { + match++ + } + } + } else { + dst.GitRepositoryReference = nil + } + + // try to unmarshal data into HelmChartRepositoryReference + err = newStrictDecoder(data).Decode(&dst.HelmChartRepositoryReference) + if err == nil { + jsonHelmChartRepositoryReference, _ := json.Marshal(dst.HelmChartRepositoryReference) + if string(jsonHelmChartRepositoryReference) == "{}" { // empty struct + dst.HelmChartRepositoryReference = nil + } else { + if err = validator.Validate(dst.HelmChartRepositoryReference); err != nil { + dst.HelmChartRepositoryReference = nil + } else { + match++ + } + } + } else { + dst.HelmChartRepositoryReference = nil + } + + if match > 1 { // more than 1 match + // reset to nil + dst.GitRepositoryReference = nil + dst.HelmChartRepositoryReference = nil + + return fmt.Errorf("data matches more than one schema in oneOf(HelmRepositoryReference)") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("data failed to match schemas in oneOf(HelmRepositoryReference)") + } +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src HelmRepositoryReference) MarshalJSON() ([]byte, error) { + if src.GitRepositoryReference != nil { + return json.Marshal(&src.GitRepositoryReference) + } + + if src.HelmChartRepositoryReference != nil { + return json.Marshal(&src.HelmChartRepositoryReference) + } + + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *HelmRepositoryReference) GetActualInstance() (interface{}) { + if obj == nil { + return nil + } + if obj.GitRepositoryReference != nil { + return obj.GitRepositoryReference + } + + if obj.HelmChartRepositoryReference != nil { + return obj.HelmChartRepositoryReference + } + + // all schemas are nil + return nil +} + +type NullableHelmRepositoryReference struct { + value *HelmRepositoryReference + isSet bool +} + +func (v NullableHelmRepositoryReference) Get() *HelmRepositoryReference { + return v.value +} + +func (v *NullableHelmRepositoryReference) Set(val *HelmRepositoryReference) { + v.value = val + v.isSet = true +} + +func (v NullableHelmRepositoryReference) IsSet() bool { + return v.isSet +} + +func (v *NullableHelmRepositoryReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableHelmRepositoryReference(val *HelmRepositoryReference) *NullableHelmRepositoryReference { + return &NullableHelmRepositoryReference{value: val, isSet: true} +} + +func (v NullableHelmRepositoryReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableHelmRepositoryReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_kustomization_reference.go b/pkg/api/model_kustomization_reference.go new file mode 100644 index 00000000..61d9f5a4 --- /dev/null +++ b/pkg/api/model_kustomization_reference.go @@ -0,0 +1,124 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "gopkg.in/validator.v2" + "fmt" +) + +// KustomizationReference - struct for KustomizationReference +type KustomizationReference struct { + GitRepositoryPathReference *GitRepositoryPathReference +} + +// GitRepositoryPathReferenceAsKustomizationReference is a convenience function that returns GitRepositoryPathReference wrapped in KustomizationReference +func GitRepositoryPathReferenceAsKustomizationReference(v *GitRepositoryPathReference) KustomizationReference { + return KustomizationReference{ + GitRepositoryPathReference: v, + } +} + + +// Unmarshal JSON data into one of the pointers in the struct +func (dst *KustomizationReference) UnmarshalJSON(data []byte) error { + var err error + match := 0 + // try to unmarshal data into GitRepositoryPathReference + err = newStrictDecoder(data).Decode(&dst.GitRepositoryPathReference) + if err == nil { + jsonGitRepositoryPathReference, _ := json.Marshal(dst.GitRepositoryPathReference) + if string(jsonGitRepositoryPathReference) == "{}" { // empty struct + dst.GitRepositoryPathReference = nil + } else { + if err = validator.Validate(dst.GitRepositoryPathReference); err != nil { + dst.GitRepositoryPathReference = nil + } else { + match++ + } + } + } else { + dst.GitRepositoryPathReference = nil + } + + if match > 1 { // more than 1 match + // reset to nil + dst.GitRepositoryPathReference = nil + + return fmt.Errorf("data matches more than one schema in oneOf(KustomizationReference)") + } else if match == 1 { + return nil // exactly one match + } else { // no match + return fmt.Errorf("data failed to match schemas in oneOf(KustomizationReference)") + } +} + +// Marshal data from the first non-nil pointers in the struct to JSON +func (src KustomizationReference) MarshalJSON() ([]byte, error) { + if src.GitRepositoryPathReference != nil { + return json.Marshal(&src.GitRepositoryPathReference) + } + + return nil, nil // no data in oneOf schemas +} + +// Get the actual instance +func (obj *KustomizationReference) GetActualInstance() (interface{}) { + if obj == nil { + return nil + } + if obj.GitRepositoryPathReference != nil { + return obj.GitRepositoryPathReference + } + + // all schemas are nil + return nil +} + +type NullableKustomizationReference struct { + value *KustomizationReference + isSet bool +} + +func (v NullableKustomizationReference) Get() *KustomizationReference { + return v.value +} + +func (v *NullableKustomizationReference) Set(val *KustomizationReference) { + v.value = val + v.isSet = true +} + +func (v NullableKustomizationReference) IsSet() bool { + return v.isSet +} + +func (v *NullableKustomizationReference) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKustomizationReference(val *KustomizationReference) *NullableKustomizationReference { + return &NullableKustomizationReference{value: val, isSet: true} +} + +func (v NullableKustomizationReference) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKustomizationReference) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_kustomize_manifest_injection.go b/pkg/api/model_kustomize_manifest_injection.go new file mode 100644 index 00000000..53a36ae8 --- /dev/null +++ b/pkg/api/model_kustomize_manifest_injection.go @@ -0,0 +1,187 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the KustomizeManifestInjection type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &KustomizeManifestInjection{} + +// KustomizeManifestInjection struct for KustomizeManifestInjection +type KustomizeManifestInjection struct { + FileName string `json:"fileName"` + Manifests []Manifest `json:"manifests"` +} + +type _KustomizeManifestInjection KustomizeManifestInjection + +// NewKustomizeManifestInjection instantiates a new KustomizeManifestInjection object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKustomizeManifestInjection(fileName string, manifests []Manifest) *KustomizeManifestInjection { + this := KustomizeManifestInjection{} + this.FileName = fileName + this.Manifests = manifests + return &this +} + +// NewKustomizeManifestInjectionWithDefaults instantiates a new KustomizeManifestInjection object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKustomizeManifestInjectionWithDefaults() *KustomizeManifestInjection { + this := KustomizeManifestInjection{} + return &this +} + +// GetFileName returns the FileName field value +func (o *KustomizeManifestInjection) GetFileName() string { + if o == nil { + var ret string + return ret + } + + return o.FileName +} + +// GetFileNameOk returns a tuple with the FileName field value +// and a boolean to check if the value has been set. +func (o *KustomizeManifestInjection) GetFileNameOk() (*string, bool) { + if o == nil { + return nil, false + } + return &o.FileName, true +} + +// SetFileName sets field value +func (o *KustomizeManifestInjection) SetFileName(v string) { + o.FileName = v +} + +// GetManifests returns the Manifests field value +func (o *KustomizeManifestInjection) GetManifests() []Manifest { + if o == nil { + var ret []Manifest + return ret + } + + return o.Manifests +} + +// GetManifestsOk returns a tuple with the Manifests field value +// and a boolean to check if the value has been set. +func (o *KustomizeManifestInjection) GetManifestsOk() ([]Manifest, bool) { + if o == nil { + return nil, false + } + return o.Manifests, true +} + +// SetManifests sets field value +func (o *KustomizeManifestInjection) SetManifests(v []Manifest) { + o.Manifests = v +} + +func (o KustomizeManifestInjection) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o KustomizeManifestInjection) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["fileName"] = o.FileName + toSerialize["manifests"] = o.Manifests + return toSerialize, nil +} + +func (o *KustomizeManifestInjection) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "fileName", + "manifests", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varKustomizeManifestInjection := _KustomizeManifestInjection{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varKustomizeManifestInjection) + + if err != nil { + return err + } + + *o = KustomizeManifestInjection(varKustomizeManifestInjection) + + return err +} + +type NullableKustomizeManifestInjection struct { + value *KustomizeManifestInjection + isSet bool +} + +func (v NullableKustomizeManifestInjection) Get() *KustomizeManifestInjection { + return v.value +} + +func (v *NullableKustomizeManifestInjection) Set(val *KustomizeManifestInjection) { + v.value = val + v.isSet = true +} + +func (v NullableKustomizeManifestInjection) IsSet() bool { + return v.isSet +} + +func (v *NullableKustomizeManifestInjection) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKustomizeManifestInjection(val *KustomizeManifestInjection) *NullableKustomizeManifestInjection { + return &NullableKustomizeManifestInjection{value: val, isSet: true} +} + +func (v NullableKustomizeManifestInjection) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKustomizeManifestInjection) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_kustomize_render_kustomization_action.go b/pkg/api/model_kustomize_render_kustomization_action.go new file mode 100644 index 00000000..4d1ac373 --- /dev/null +++ b/pkg/api/model_kustomize_render_kustomization_action.go @@ -0,0 +1,195 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the KustomizeRenderKustomizationAction type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &KustomizeRenderKustomizationAction{} + +// KustomizeRenderKustomizationAction struct for KustomizeRenderKustomizationAction +type KustomizeRenderKustomizationAction struct { + Reference KustomizationReference `json:"reference"` + Parameters *KustomizeRenderParameters `json:"parameters,omitempty"` +} + +type _KustomizeRenderKustomizationAction KustomizeRenderKustomizationAction + +// NewKustomizeRenderKustomizationAction instantiates a new KustomizeRenderKustomizationAction object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKustomizeRenderKustomizationAction(reference KustomizationReference) *KustomizeRenderKustomizationAction { + this := KustomizeRenderKustomizationAction{} + this.Reference = reference + return &this +} + +// NewKustomizeRenderKustomizationActionWithDefaults instantiates a new KustomizeRenderKustomizationAction object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKustomizeRenderKustomizationActionWithDefaults() *KustomizeRenderKustomizationAction { + this := KustomizeRenderKustomizationAction{} + return &this +} + +// GetReference returns the Reference field value +func (o *KustomizeRenderKustomizationAction) GetReference() KustomizationReference { + if o == nil { + var ret KustomizationReference + return ret + } + + return o.Reference +} + +// GetReferenceOk returns a tuple with the Reference field value +// and a boolean to check if the value has been set. +func (o *KustomizeRenderKustomizationAction) GetReferenceOk() (*KustomizationReference, bool) { + if o == nil { + return nil, false + } + return &o.Reference, true +} + +// SetReference sets field value +func (o *KustomizeRenderKustomizationAction) SetReference(v KustomizationReference) { + o.Reference = v +} + +// GetParameters returns the Parameters field value if set, zero value otherwise. +func (o *KustomizeRenderKustomizationAction) GetParameters() KustomizeRenderParameters { + if o == nil || IsNil(o.Parameters) { + var ret KustomizeRenderParameters + return ret + } + return *o.Parameters +} + +// GetParametersOk returns a tuple with the Parameters field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KustomizeRenderKustomizationAction) GetParametersOk() (*KustomizeRenderParameters, bool) { + if o == nil || IsNil(o.Parameters) { + return nil, false + } + return o.Parameters, true +} + +// HasParameters returns a boolean if a field has been set. +func (o *KustomizeRenderKustomizationAction) HasParameters() bool { + if o != nil && !IsNil(o.Parameters) { + return true + } + + return false +} + +// SetParameters gets a reference to the given KustomizeRenderParameters and assigns it to the Parameters field. +func (o *KustomizeRenderKustomizationAction) SetParameters(v KustomizeRenderParameters) { + o.Parameters = &v +} + +func (o KustomizeRenderKustomizationAction) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o KustomizeRenderKustomizationAction) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + toSerialize["reference"] = o.Reference + if !IsNil(o.Parameters) { + toSerialize["parameters"] = o.Parameters + } + return toSerialize, nil +} + +func (o *KustomizeRenderKustomizationAction) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "reference", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varKustomizeRenderKustomizationAction := _KustomizeRenderKustomizationAction{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varKustomizeRenderKustomizationAction) + + if err != nil { + return err + } + + *o = KustomizeRenderKustomizationAction(varKustomizeRenderKustomizationAction) + + return err +} + +type NullableKustomizeRenderKustomizationAction struct { + value *KustomizeRenderKustomizationAction + isSet bool +} + +func (v NullableKustomizeRenderKustomizationAction) Get() *KustomizeRenderKustomizationAction { + return v.value +} + +func (v *NullableKustomizeRenderKustomizationAction) Set(val *KustomizeRenderKustomizationAction) { + v.value = val + v.isSet = true +} + +func (v NullableKustomizeRenderKustomizationAction) IsSet() bool { + return v.isSet +} + +func (v *NullableKustomizeRenderKustomizationAction) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKustomizeRenderKustomizationAction(val *KustomizeRenderKustomizationAction) *NullableKustomizeRenderKustomizationAction { + return &NullableKustomizeRenderKustomizationAction{value: val, isSet: true} +} + +func (v NullableKustomizeRenderKustomizationAction) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKustomizeRenderKustomizationAction) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_kustomize_render_kustomization_action_response.go b/pkg/api/model_kustomize_render_kustomization_action_response.go new file mode 100644 index 00000000..76bdae40 --- /dev/null +++ b/pkg/api/model_kustomize_render_kustomization_action_response.go @@ -0,0 +1,127 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// checks if the KustomizeRenderKustomizationActionResponse type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &KustomizeRenderKustomizationActionResponse{} + +// KustomizeRenderKustomizationActionResponse struct for KustomizeRenderKustomizationActionResponse +type KustomizeRenderKustomizationActionResponse struct { + Manifests []Manifest `json:"manifests,omitempty"` +} + +// NewKustomizeRenderKustomizationActionResponse instantiates a new KustomizeRenderKustomizationActionResponse object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKustomizeRenderKustomizationActionResponse() *KustomizeRenderKustomizationActionResponse { + this := KustomizeRenderKustomizationActionResponse{} + return &this +} + +// NewKustomizeRenderKustomizationActionResponseWithDefaults instantiates a new KustomizeRenderKustomizationActionResponse object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKustomizeRenderKustomizationActionResponseWithDefaults() *KustomizeRenderKustomizationActionResponse { + this := KustomizeRenderKustomizationActionResponse{} + return &this +} + +// GetManifests returns the Manifests field value if set, zero value otherwise. +func (o *KustomizeRenderKustomizationActionResponse) GetManifests() []Manifest { + if o == nil || IsNil(o.Manifests) { + var ret []Manifest + return ret + } + return o.Manifests +} + +// GetManifestsOk returns a tuple with the Manifests field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KustomizeRenderKustomizationActionResponse) GetManifestsOk() ([]Manifest, bool) { + if o == nil || IsNil(o.Manifests) { + return nil, false + } + return o.Manifests, true +} + +// HasManifests returns a boolean if a field has been set. +func (o *KustomizeRenderKustomizationActionResponse) HasManifests() bool { + if o != nil && !IsNil(o.Manifests) { + return true + } + + return false +} + +// SetManifests gets a reference to the given []Manifest and assigns it to the Manifests field. +func (o *KustomizeRenderKustomizationActionResponse) SetManifests(v []Manifest) { + o.Manifests = v +} + +func (o KustomizeRenderKustomizationActionResponse) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o KustomizeRenderKustomizationActionResponse) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Manifests) { + toSerialize["manifests"] = o.Manifests + } + return toSerialize, nil +} + +type NullableKustomizeRenderKustomizationActionResponse struct { + value *KustomizeRenderKustomizationActionResponse + isSet bool +} + +func (v NullableKustomizeRenderKustomizationActionResponse) Get() *KustomizeRenderKustomizationActionResponse { + return v.value +} + +func (v *NullableKustomizeRenderKustomizationActionResponse) Set(val *KustomizeRenderKustomizationActionResponse) { + v.value = val + v.isSet = true +} + +func (v NullableKustomizeRenderKustomizationActionResponse) IsSet() bool { + return v.isSet +} + +func (v *NullableKustomizeRenderKustomizationActionResponse) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKustomizeRenderKustomizationActionResponse(val *KustomizeRenderKustomizationActionResponse) *NullableKustomizeRenderKustomizationActionResponse { + return &NullableKustomizeRenderKustomizationActionResponse{value: val, isSet: true} +} + +func (v NullableKustomizeRenderKustomizationActionResponse) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKustomizeRenderKustomizationActionResponse) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_kustomize_render_parameters.go b/pkg/api/model_kustomize_render_parameters.go new file mode 100644 index 00000000..a2c43d4a --- /dev/null +++ b/pkg/api/model_kustomize_render_parameters.go @@ -0,0 +1,127 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" +) + +// checks if the KustomizeRenderParameters type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &KustomizeRenderParameters{} + +// KustomizeRenderParameters struct for KustomizeRenderParameters +type KustomizeRenderParameters struct { + ManifestInjections []KustomizeManifestInjection `json:"manifestInjections,omitempty"` +} + +// NewKustomizeRenderParameters instantiates a new KustomizeRenderParameters object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewKustomizeRenderParameters() *KustomizeRenderParameters { + this := KustomizeRenderParameters{} + return &this +} + +// NewKustomizeRenderParametersWithDefaults instantiates a new KustomizeRenderParameters object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewKustomizeRenderParametersWithDefaults() *KustomizeRenderParameters { + this := KustomizeRenderParameters{} + return &this +} + +// GetManifestInjections returns the ManifestInjections field value if set, zero value otherwise. +func (o *KustomizeRenderParameters) GetManifestInjections() []KustomizeManifestInjection { + if o == nil || IsNil(o.ManifestInjections) { + var ret []KustomizeManifestInjection + return ret + } + return o.ManifestInjections +} + +// GetManifestInjectionsOk returns a tuple with the ManifestInjections field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *KustomizeRenderParameters) GetManifestInjectionsOk() ([]KustomizeManifestInjection, bool) { + if o == nil || IsNil(o.ManifestInjections) { + return nil, false + } + return o.ManifestInjections, true +} + +// HasManifestInjections returns a boolean if a field has been set. +func (o *KustomizeRenderParameters) HasManifestInjections() bool { + if o != nil && !IsNil(o.ManifestInjections) { + return true + } + + return false +} + +// SetManifestInjections gets a reference to the given []KustomizeManifestInjection and assigns it to the ManifestInjections field. +func (o *KustomizeRenderParameters) SetManifestInjections(v []KustomizeManifestInjection) { + o.ManifestInjections = v +} + +func (o KustomizeRenderParameters) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o KustomizeRenderParameters) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.ManifestInjections) { + toSerialize["manifestInjections"] = o.ManifestInjections + } + return toSerialize, nil +} + +type NullableKustomizeRenderParameters struct { + value *KustomizeRenderParameters + isSet bool +} + +func (v NullableKustomizeRenderParameters) Get() *KustomizeRenderParameters { + return v.value +} + +func (v *NullableKustomizeRenderParameters) Set(val *KustomizeRenderParameters) { + v.value = val + v.isSet = true +} + +func (v NullableKustomizeRenderParameters) IsSet() bool { + return v.isSet +} + +func (v *NullableKustomizeRenderParameters) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableKustomizeRenderParameters(val *KustomizeRenderParameters) *NullableKustomizeRenderParameters { + return &NullableKustomizeRenderParameters{value: val, isSet: true} +} + +func (v NullableKustomizeRenderParameters) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableKustomizeRenderParameters) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/model_manifest.go b/pkg/api/model_manifest.go new file mode 100644 index 00000000..8a481ccc --- /dev/null +++ b/pkg/api/model_manifest.go @@ -0,0 +1,195 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "encoding/json" + "bytes" + "fmt" +) + +// checks if the Manifest type satisfies the MappedNullable interface at compile time +var _ MappedNullable = &Manifest{} + +// Manifest struct for Manifest +type Manifest struct { + Source *string `json:"source,omitempty"` + Content map[string]interface{} `json:"content"` +} + +type _Manifest Manifest + +// NewManifest instantiates a new Manifest object +// This constructor will assign default values to properties that have it defined, +// and makes sure properties required by API are set, but the set of arguments +// will change when the set of required properties is changed +func NewManifest(content map[string]interface{}) *Manifest { + this := Manifest{} + this.Content = content + return &this +} + +// NewManifestWithDefaults instantiates a new Manifest object +// This constructor will only assign default values to properties that have it defined, +// but it doesn't guarantee that properties required by API are set +func NewManifestWithDefaults() *Manifest { + this := Manifest{} + return &this +} + +// GetSource returns the Source field value if set, zero value otherwise. +func (o *Manifest) GetSource() string { + if o == nil || IsNil(o.Source) { + var ret string + return ret + } + return *o.Source +} + +// GetSourceOk returns a tuple with the Source field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *Manifest) GetSourceOk() (*string, bool) { + if o == nil || IsNil(o.Source) { + return nil, false + } + return o.Source, true +} + +// HasSource returns a boolean if a field has been set. +func (o *Manifest) HasSource() bool { + if o != nil && !IsNil(o.Source) { + return true + } + + return false +} + +// SetSource gets a reference to the given string and assigns it to the Source field. +func (o *Manifest) SetSource(v string) { + o.Source = &v +} + +// GetContent returns the Content field value +func (o *Manifest) GetContent() map[string]interface{} { + if o == nil { + var ret map[string]interface{} + return ret + } + + return o.Content +} + +// GetContentOk returns a tuple with the Content field value +// and a boolean to check if the value has been set. +func (o *Manifest) GetContentOk() (map[string]interface{}, bool) { + if o == nil { + return map[string]interface{}{}, false + } + return o.Content, true +} + +// SetContent sets field value +func (o *Manifest) SetContent(v map[string]interface{}) { + o.Content = v +} + +func (o Manifest) MarshalJSON() ([]byte, error) { + toSerialize,err := o.ToMap() + if err != nil { + return []byte{}, err + } + return json.Marshal(toSerialize) +} + +func (o Manifest) ToMap() (map[string]interface{}, error) { + toSerialize := map[string]interface{}{} + if !IsNil(o.Source) { + toSerialize["source"] = o.Source + } + toSerialize["content"] = o.Content + return toSerialize, nil +} + +func (o *Manifest) UnmarshalJSON(data []byte) (err error) { + // This validates that all required properties are included in the JSON object + // by unmarshalling the object into a generic map with string keys and checking + // that every required field exists as a key in the generic map. + requiredProperties := []string{ + "content", + } + + allProperties := make(map[string]interface{}) + + err = json.Unmarshal(data, &allProperties) + + if err != nil { + return err; + } + + for _, requiredProperty := range(requiredProperties) { + if _, exists := allProperties[requiredProperty]; !exists { + return fmt.Errorf("no value given for required property %v", requiredProperty) + } + } + + varManifest := _Manifest{} + + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + err = decoder.Decode(&varManifest) + + if err != nil { + return err + } + + *o = Manifest(varManifest) + + return err +} + +type NullableManifest struct { + value *Manifest + isSet bool +} + +func (v NullableManifest) Get() *Manifest { + return v.value +} + +func (v *NullableManifest) Set(val *Manifest) { + v.value = val + v.isSet = true +} + +func (v NullableManifest) IsSet() bool { + return v.isSet +} + +func (v *NullableManifest) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableManifest(val *Manifest) *NullableManifest { + return &NullableManifest{value: val, isSet: true} +} + +func (v NullableManifest) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableManifest) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + + diff --git a/pkg/api/response.go b/pkg/api/response.go new file mode 100644 index 00000000..49b07640 --- /dev/null +++ b/pkg/api/response.go @@ -0,0 +1,48 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "net/http" +) + +// APIResponse stores the API response returned by the server. +type APIResponse struct { + *http.Response `json:"-"` + Message string `json:"message,omitempty"` + // Operation is the name of the OpenAPI operation. + Operation string `json:"operation,omitempty"` + // RequestURL is the request URL. This value is always available, even if the + // embedded *http.Response is nil. + RequestURL string `json:"url,omitempty"` + // Method is the HTTP method used for the request. This value is always + // available, even if the embedded *http.Response is nil. + Method string `json:"method,omitempty"` + // Payload holds the contents of the response body (which may be nil or empty). + // This is provided here as the raw response.Body() reader will have already + // been drained. + Payload []byte `json:"-"` +} + +// NewAPIResponse returns a new APIResponse object. +func NewAPIResponse(r *http.Response) *APIResponse { + + response := &APIResponse{Response: r} + return response +} + +// NewAPIResponseWithError returns a new APIResponse object with the provided error message. +func NewAPIResponseWithError(errorMessage string) *APIResponse { + + response := &APIResponse{Message: errorMessage} + return response +} diff --git a/pkg/api/utils.go b/pkg/api/utils.go new file mode 100644 index 00000000..94b67622 --- /dev/null +++ b/pkg/api/utils.go @@ -0,0 +1,362 @@ +/* +Manifest Maestro + +Renders Kubernetes manifests with the help of various tools such as Helm and Kustomize. + +API version: v1 +Contact: e.rieb@posteo.de +*/ + +// Code generated by OpenAPI Generator (https://openapi-generator.tech); DO NOT EDIT. + +package api + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" + "time" +) + +// PtrBool is a helper routine that returns a pointer to given boolean value. +func PtrBool(v bool) *bool { return &v } + +// PtrInt is a helper routine that returns a pointer to given integer value. +func PtrInt(v int) *int { return &v } + +// PtrInt32 is a helper routine that returns a pointer to given integer value. +func PtrInt32(v int32) *int32 { return &v } + +// PtrInt64 is a helper routine that returns a pointer to given integer value. +func PtrInt64(v int64) *int64 { return &v } + +// PtrFloat32 is a helper routine that returns a pointer to given float value. +func PtrFloat32(v float32) *float32 { return &v } + +// PtrFloat64 is a helper routine that returns a pointer to given float value. +func PtrFloat64(v float64) *float64 { return &v } + +// PtrString is a helper routine that returns a pointer to given string value. +func PtrString(v string) *string { return &v } + +// PtrTime is helper routine that returns a pointer to given Time value. +func PtrTime(v time.Time) *time.Time { return &v } + +type NullableBool struct { + value *bool + isSet bool +} + +func (v NullableBool) Get() *bool { + return v.value +} + +func (v *NullableBool) Set(val *bool) { + v.value = val + v.isSet = true +} + +func (v NullableBool) IsSet() bool { + return v.isSet +} + +func (v *NullableBool) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableBool(val *bool) *NullableBool { + return &NullableBool{value: val, isSet: true} +} + +func (v NullableBool) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableBool) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt struct { + value *int + isSet bool +} + +func (v NullableInt) Get() *int { + return v.value +} + +func (v *NullableInt) Set(val *int) { + v.value = val + v.isSet = true +} + +func (v NullableInt) IsSet() bool { + return v.isSet +} + +func (v *NullableInt) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt(val *int) *NullableInt { + return &NullableInt{value: val, isSet: true} +} + +func (v NullableInt) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt32 struct { + value *int32 + isSet bool +} + +func (v NullableInt32) Get() *int32 { + return v.value +} + +func (v *NullableInt32) Set(val *int32) { + v.value = val + v.isSet = true +} + +func (v NullableInt32) IsSet() bool { + return v.isSet +} + +func (v *NullableInt32) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt32(val *int32) *NullableInt32 { + return &NullableInt32{value: val, isSet: true} +} + +func (v NullableInt32) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt32) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableInt64 struct { + value *int64 + isSet bool +} + +func (v NullableInt64) Get() *int64 { + return v.value +} + +func (v *NullableInt64) Set(val *int64) { + v.value = val + v.isSet = true +} + +func (v NullableInt64) IsSet() bool { + return v.isSet +} + +func (v *NullableInt64) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableInt64(val *int64) *NullableInt64 { + return &NullableInt64{value: val, isSet: true} +} + +func (v NullableInt64) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableInt64) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableFloat32 struct { + value *float32 + isSet bool +} + +func (v NullableFloat32) Get() *float32 { + return v.value +} + +func (v *NullableFloat32) Set(val *float32) { + v.value = val + v.isSet = true +} + +func (v NullableFloat32) IsSet() bool { + return v.isSet +} + +func (v *NullableFloat32) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableFloat32(val *float32) *NullableFloat32 { + return &NullableFloat32{value: val, isSet: true} +} + +func (v NullableFloat32) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableFloat32) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableFloat64 struct { + value *float64 + isSet bool +} + +func (v NullableFloat64) Get() *float64 { + return v.value +} + +func (v *NullableFloat64) Set(val *float64) { + v.value = val + v.isSet = true +} + +func (v NullableFloat64) IsSet() bool { + return v.isSet +} + +func (v *NullableFloat64) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableFloat64(val *float64) *NullableFloat64 { + return &NullableFloat64{value: val, isSet: true} +} + +func (v NullableFloat64) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableFloat64) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableString struct { + value *string + isSet bool +} + +func (v NullableString) Get() *string { + return v.value +} + +func (v *NullableString) Set(val *string) { + v.value = val + v.isSet = true +} + +func (v NullableString) IsSet() bool { + return v.isSet +} + +func (v *NullableString) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableString(val *string) *NullableString { + return &NullableString{value: val, isSet: true} +} + +func (v NullableString) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableString) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +type NullableTime struct { + value *time.Time + isSet bool +} + +func (v NullableTime) Get() *time.Time { + return v.value +} + +func (v *NullableTime) Set(val *time.Time) { + v.value = val + v.isSet = true +} + +func (v NullableTime) IsSet() bool { + return v.isSet +} + +func (v *NullableTime) Unset() { + v.value = nil + v.isSet = false +} + +func NewNullableTime(val *time.Time) *NullableTime { + return &NullableTime{value: val, isSet: true} +} + +func (v NullableTime) MarshalJSON() ([]byte, error) { + return json.Marshal(v.value) +} + +func (v *NullableTime) UnmarshalJSON(src []byte) error { + v.isSet = true + return json.Unmarshal(src, &v.value) +} + +// IsNil checks if an input is nil +func IsNil(i interface{}) bool { + if i == nil { + return true + } + switch reflect.TypeOf(i).Kind() { + case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.UnsafePointer, reflect.Interface, reflect.Slice: + return reflect.ValueOf(i).IsNil() + case reflect.Array: + return reflect.ValueOf(i).IsZero() + } + return false +} + +type MappedNullable interface { + ToMap() (map[string]interface{}, error) +} + +// A wrapper for strict JSON decoding +func newStrictDecoder(data []byte) *json.Decoder { + dec := json.NewDecoder(bytes.NewBuffer(data)) + dec.DisallowUnknownFields() + return dec +} + +// Prevent trying to import "fmt" +func reportError(format string, a ...interface{}) error { + return fmt.Errorf(format, a...) +} \ No newline at end of file diff --git a/pkg/utils/commonutils/commonutils.go b/pkg/utils/commonutils/commonutils.go new file mode 100644 index 00000000..ae6606e4 --- /dev/null +++ b/pkg/utils/commonutils/commonutils.go @@ -0,0 +1,23 @@ +package commonutils + +func DefaultIfNil[V comparable](value *V, defaultValue V) V { + if value == nil { + return defaultValue + } + return *value +} + +func DefaultIfEmpty[V comparable](value *V, defaultValue V) V { + if value == nil || *value == *new(V) { + return defaultValue + } + return *value +} + +func IsEmpty[V comparable](value *V) bool { + return value == nil || *value == *new(V) +} + +func Ptr[V any](value V) *V { + return &value +} diff --git a/pkg/utils/filesystem/filesystem.go b/pkg/utils/filesystem/filesystem.go new file mode 100644 index 00000000..9fec8996 --- /dev/null +++ b/pkg/utils/filesystem/filesystem.go @@ -0,0 +1,84 @@ +package filesystem + +import ( + "github.com/go-git/go-billy/v5" + "io" + "path/filepath" + + "sigs.k8s.io/kustomize/kyaml/filesys" +) + +func (f *FileSystem) SkipDir() error { + return filepath.SkipDir +} + +func (f *FileSystem) Dir(path string) string { + return filepath.Dir(path) +} + +func (f *FileSystem) Join(elem ...string) string { + return filepath.Join(elem...) +} + +func (f *FileSystem) IsAbs(path string) bool { + cleanPath := filepath.Clean(path) + return len(cleanPath) > 0 && string(cleanPath[0]) == f.Root +} + +func New() *FileSystem { + return &FileSystem{ + Root: string(filepath.Separator), + Separator: string(filepath.Separator), + FileSystem: filesys.MakeFsInMemory(), + } +} + +type FileSystem struct { + Root string + Separator string + filesys.FileSystem +} + +func CopyFromBillyToFileSystem(origin billy.Filesystem, fileSystem *FileSystem) error { + var copyRecursively func(currentPath string) error + copyRecursively = func(currentPath string) error { + files, err := origin.ReadDir(currentPath) + if err != nil { + return err + } + + for _, file := range files { + fileName := fileSystem.Join(currentPath, file.Name()) + if file.IsDir() { + if innerErr := fileSystem.MkdirAll(fileName); innerErr != nil { + return innerErr + } + if innerErr := copyRecursively(fileName); innerErr != nil { + return innerErr + } + } else { + src, innerErr := origin.Open(fileName) + if innerErr != nil { + return innerErr + } + + dst, innerErr := fileSystem.Create(fileName) + if innerErr != nil { + return innerErr + } + + if _, innerErr = io.Copy(dst, src); innerErr != nil { + return innerErr + } + if innerErr = dst.Close(); innerErr != nil { + return innerErr + } + if innerErr = src.Close(); innerErr != nil { + return innerErr + } + } + } + return nil + } + return copyRecursively("") +} diff --git a/pkg/utils/maputils/maputils.go b/pkg/utils/maputils/maputils.go new file mode 100644 index 00000000..31c23711 --- /dev/null +++ b/pkg/utils/maputils/maputils.go @@ -0,0 +1,27 @@ +package maputils + +func DeepMerge(a, b map[string]any) map[string]any { + out := make(map[string]any, len(a)) + for key, value := range a { + out[key] = value + } + for key, value := range b { + valueAsMap, ok := value.(map[string]any) + if !ok { + out[key] = value + continue + } + outValue, ok := out[key] + if !ok { + out[key] = value + continue + } + outValueAsMap, ok := outValue.(map[string]any) + if !ok { + out[key] = value + continue + } + out[key] = DeepMerge(outValueAsMap, valueAsMap) + } + return out +} diff --git a/pkg/utils/sliceutils/sliceutils.go b/pkg/utils/sliceutils/sliceutils.go new file mode 100644 index 00000000..2ed2a43a --- /dev/null +++ b/pkg/utils/sliceutils/sliceutils.go @@ -0,0 +1,18 @@ +package sliceutils + +func Unique[V comparable](values []V) []V { + indicators := IndicatorMap(values) + unique := make([]V, 0) + for value := range indicators { + unique = append(unique, value) + } + return unique +} + +func IndicatorMap[V comparable](values []V) map[V]bool { + marker := make(map[V]bool) + for _, value := range values { + marker[value] = true + } + return marker +} diff --git a/pkg/utils/stringutils/stringutils.go b/pkg/utils/stringutils/stringutils.go new file mode 100644 index 00000000..85146399 --- /dev/null +++ b/pkg/utils/stringutils/stringutils.go @@ -0,0 +1,20 @@ +package stringutils + +import ( + "strings" + + "github.com/Roshick/manifest-maestro/pkg/utils/sliceutils" +) + +func SplitUniqueNonEmpty(raw string, separator string) []string { + values := strings.Split(raw, separator) + k := 0 + for i, n := range values { + values[i] = strings.TrimSpace(values[i]) + if values[i] != "" { + values[k] = n + k++ + } + } + return sliceutils.Unique(values[:k]) +} diff --git a/pkg/utils/targz/targz.go b/pkg/utils/targz/targz.go new file mode 100644 index 00000000..c5d14587 --- /dev/null +++ b/pkg/utils/targz/targz.go @@ -0,0 +1,114 @@ +package targz + +import ( + "archive/tar" + "compress/gzip" + "context" + "fmt" + "io" + "io/fs" + "strings" + + "github.com/Roshick/manifest-maestro/pkg/utils/filesystem" + aulogging "github.com/StephanHCB/go-autumn-logging" +) + +func Compress(ctx context.Context, fileSystem *filesystem.FileSystem, sourcePath string, targetSubPath string, targetWriter io.Writer) error { + if !fileSystem.Exists(sourcePath) { + return fmt.Errorf("ToDo does not exist %s", sourcePath) + } + + gzw := gzip.NewWriter(targetWriter) + defer func() { + if err := gzw.Close(); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("failed to close gzip writer for '%s'", sourcePath) + } + }() + tw := tar.NewWriter(gzw) + defer func() { + if err := tw.Close(); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("failed to close tar writer for '%s'", sourcePath) + } + }() + + return fileSystem.Walk(sourcePath, func(filePath string, fileInfo fs.FileInfo, err error) error { + if err != nil { + return err + } + if !fileInfo.Mode().IsRegular() { + return fmt.Errorf("cannot compress irregular file '%s'", filePath) + } + if fileInfo.IsDir() { + return nil + } + + header, err := tar.FileInfoHeader(fileInfo, fileInfo.Name()) + if err != nil { + return err + } + header.Name = strings.TrimPrefix(filePath, sourcePath) + header.Name = strings.TrimPrefix(header.Name, fileSystem.Separator) + header.Name = fileSystem.Join(targetSubPath, header.Name) + if err = tw.WriteHeader(header); err != nil { + return err + } + + file, err := fileSystem.Open(filePath) + if err != nil { + return err + } + if _, err = io.Copy(tw, file); err != nil { + return err + } + if err = file.Close(); err != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(err).Printf("failed to close '%s'", filePath) + } + return nil + }) +} + +func Extract(ctx context.Context, fileSystem *filesystem.FileSystem, sourceReader io.Reader, targetPath string) error { + if err := fileSystem.MkdirAll(targetPath); err != nil { + return err + } + + gzr, err := gzip.NewReader(sourceReader) + if err != nil { + return err + } + tr := tar.NewReader(gzr) + + for { + header, innerErr := tr.Next() + if innerErr == io.EOF { + break + } + if innerErr != nil { + return innerErr + } + + filePath := fileSystem.Join(targetPath, header.Name) + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + if innerErr2 := fileSystem.MkdirAll(fileSystem.Dir(filePath)); innerErr2 != nil { + return innerErr2 + } + file, innerErr2 := fileSystem.Create(filePath) + if innerErr2 != nil { + return innerErr2 + } + if _, innerErr2 = io.Copy(file, tr); innerErr2 != nil { + return innerErr2 + } + if innerErr2 = file.Close(); innerErr2 != nil { + aulogging.Logger.Ctx(ctx).Warn().WithErr(innerErr2).Printf("failed to close '%s'", filePath) + } + default: + return fmt.Errorf("failed to extract file '%s' of unsupported type from '%s'", header.Name, targetPath) + } + } + + return nil +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..f4c08aa0 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,946 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@openapitools/openapi-generator-cli': + specifier: ^2.13.4 + version: 2.13.4 + +packages: + + '@babel/runtime@7.25.0': + resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} + engines: {node: '>=6.9.0'} + + '@lukeed/csprng@1.1.0': + resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==} + engines: {node: '>=8'} + + '@nestjs/axios@3.0.2': + resolution: {integrity: sha512-Z6GuOUdNQjP7FX+OuV2Ybyamse+/e0BFdTWBX5JxpBDKA+YkdLynDgG6HTF04zy6e9zPa19UX0WA2VDoehwhXQ==} + peerDependencies: + '@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 + axios: ^1.3.1 + rxjs: ^6.0.0 || ^7.0.0 + + '@nestjs/common@10.3.0': + resolution: {integrity: sha512-DGv34UHsZBxCM3H5QGE2XE/+oLJzz5+714JQjBhjD9VccFlQs3LRxo/epso4l7nJIiNlZkPyIUC8WzfU/5RTsQ==} + peerDependencies: + class-transformer: '*' + class-validator: '*' + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + class-transformer: + optional: true + class-validator: + optional: true + + '@nestjs/core@10.3.0': + resolution: {integrity: sha512-N06P5ncknW/Pm8bj964WvLIZn2gNhHliCBoAO1LeBvNImYkecqKcrmLbY49Fa1rmMfEM3MuBHeDys3edeuYAOA==} + peerDependencies: + '@nestjs/common': ^10.0.0 + '@nestjs/microservices': ^10.0.0 + '@nestjs/platform-express': ^10.0.0 + '@nestjs/websockets': ^10.0.0 + reflect-metadata: ^0.1.12 + rxjs: ^7.1.0 + peerDependenciesMeta: + '@nestjs/microservices': + optional: true + '@nestjs/platform-express': + optional: true + '@nestjs/websockets': + optional: true + + '@nuxtjs/opencollective@0.3.2': + resolution: {integrity: sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + '@openapitools/openapi-generator-cli@2.13.4': + resolution: {integrity: sha512-4JKyrk55ohQK2FcuZbPdNvxdyXD14jjOIvE8hYjJ+E1cHbRbfXQXbYnjTODFE52Gx8eAxz8C9icuhDYDLn7nww==} + engines: {node: '>=10.0.0'} + hasBin: true + + agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.6.8: + resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + compare-versions@4.1.4: + resolution: {integrity: sha512-FemMreK9xNyL8gQevsdRMrvO4lFCkQP7qbuktn1q8ndcNk1+0mz7lgE7b/sNvbhVgY4w6tMN1FDp6aADjqw2rw==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concurrently@6.5.1: + resolution: {integrity: sha512-FlSwNpGjWQfRwPLXvJ/OgysbBxPkWpiVjy1042b0U7on7S7qwwMIILRj7WTN1mTgqa582bG6NFuScOoh6Zgdag==} + engines: {node: '>=10.0.0'} + hasBin: true + + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + console.table@0.10.0: + resolution: {integrity: sha512-dPyZofqggxuvSf7WXvNjuRfnsOk1YazkVP8FdxH4tcH2c37wc79/Yl6Bhr7Lsu00KMgy2ql/qCMuNu8xctZM8g==} + engines: {node: '> 0.10'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + easy-table@1.1.0: + resolution: {integrity: sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-safe-stringify@2.1.1: + resolution: {integrity: sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==} + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + follow-redirects@1.15.6: + resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + https-proxy-agent@7.0.4: + resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + engines: {node: '>= 14'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-gitrepositorycache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + iterare@1.2.1: + resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==} + engines: {node: '>=6'} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + reflect-metadata@0.1.13: + resolution: {integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + uid@2.0.2: + resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} + engines: {node: '>=8'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + +snapshots: + + '@babel/runtime@7.25.0': + dependencies: + regenerator-runtime: 0.14.1 + + '@lukeed/csprng@1.1.0': {} + + '@nestjs/axios@3.0.2(@nestjs/common@10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1)': + dependencies: + '@nestjs/common': 10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1) + axios: 1.6.8 + rxjs: 7.8.1 + + '@nestjs/common@10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1)': + dependencies: + iterare: 1.2.1 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + + '@nestjs/core@10.3.0(@nestjs/common@10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1))(reflect-metadata@0.1.13)(rxjs@7.8.1)': + dependencies: + '@nestjs/common': 10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + fast-safe-stringify: 2.1.1 + iterare: 1.2.1 + path-to-regexp: 3.2.0 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + uid: 2.0.2 + transitivePeerDependencies: + - encoding + + '@nuxtjs/opencollective@0.3.2': + dependencies: + chalk: 4.1.2 + consola: 2.15.3 + node-fetch: 2.7.0 + transitivePeerDependencies: + - encoding + + '@openapitools/openapi-generator-cli@2.13.4': + dependencies: + '@nestjs/axios': 3.0.2(@nestjs/common@10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1))(axios@1.6.8)(rxjs@7.8.1) + '@nestjs/common': 10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nestjs/core': 10.3.0(@nestjs/common@10.3.0(reflect-metadata@0.1.13)(rxjs@7.8.1))(reflect-metadata@0.1.13)(rxjs@7.8.1) + '@nuxtjs/opencollective': 0.3.2 + axios: 1.6.8 + chalk: 4.1.2 + commander: 8.3.0 + compare-versions: 4.1.4 + concurrently: 6.5.1 + console.table: 0.10.0 + fs-extra: 10.1.0 + glob: 7.2.3 + https-proxy-agent: 7.0.4 + inquirer: 8.2.6 + lodash: 4.17.21 + reflect-metadata: 0.1.13 + rxjs: 7.8.1 + tslib: 2.6.2 + transitivePeerDependencies: + - '@nestjs/microservices' + - '@nestjs/platform-express' + - '@nestjs/websockets' + - class-transformer + - class-validator + - debug + - encoding + - supports-color + + agent-base@7.1.1: + dependencies: + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + asynckit@0.4.0: {} + + axios@1.6.8: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + base64-js@1.5.1: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chardet@0.7.0: {} + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-width@3.0.0: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@1.0.4: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@8.3.0: {} + + compare-versions@4.1.4: {} + + concat-map@0.0.1: {} + + concurrently@6.5.1: + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 6.6.7 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 16.2.0 + + consola@2.15.3: {} + + console.table@0.10.0: + dependencies: + easy-table: 1.1.0 + + date-fns@2.30.0: + dependencies: + '@babel/runtime': 7.25.0 + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + delayed-stream@1.0.0: {} + + easy-table@1.1.0: + optionalDependencies: + wcwidth: 1.0.1 + + emoji-regex@8.0.0: {} + + escalade@3.1.2: {} + + escape-string-regexp@1.0.5: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-safe-stringify@2.1.1: {} + + figures@3.2.0: + dependencies: + escape-string-regexp: 1.0.5 + + follow-redirects@1.15.6: {} + + form-data@4.0.0: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + mime-types: 2.1.35 + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fs.realpath@1.0.0: {} + + get-caller-file@2.0.5: {} + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + https-proxy-agent@7.0.4: + dependencies: + agent-base: 7.1.1 + debug: 4.3.6 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + ieee754@1.2.1: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inquirer@8.2.6: + dependencies: + ansi-escapes: 4.3.2 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-width: 3.0.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.21 + mute-stream: 0.0.8 + ora: 5.4.1 + run-async: 2.4.1 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + through: 2.3.8 + wrap-ansi: 6.2.0 + + is-fullwidth-code-point@3.0.0: {} + + is-interactive@1.0.0: {} + + is-unicode-supported@0.1.0: {} + + iterare@1.2.1: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + ms@2.1.2: {} + + mute-stream@0.0.8: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + os-tmpdir@1.0.2: {} + + path-is-absolute@1.0.1: {} + + path-to-regexp@3.2.0: {} + + proxy-from-env@1.1.0: {} + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + reflect-metadata@0.1.13: {} + + regenerator-runtime@0.14.1: {} + + require-directory@2.1.1: {} + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + run-async@2.4.1: {} + + rxjs@6.6.7: + dependencies: + tslib: 1.14.1 + + rxjs@7.8.1: + dependencies: + tslib: 2.6.2 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + signal-exit@3.0.7: {} + + spawn-command@0.0.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + through@2.3.8: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + tr46@0.0.3: {} + + tree-kill@1.2.2: {} + + tslib@1.14.1: {} + + tslib@2.6.2: {} + + type-fest@0.21.3: {} + + uid@2.0.2: + dependencies: + '@lukeed/csprng': 1.1.0 + + universalify@2.0.1: {} + + util-deprecate@1.0.2: {} + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yargs-parser@20.2.9: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.1.2 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 diff --git a/test/mock/clockmock/clock.go b/test/mock/clockmock/clock.go new file mode 100755 index 00000000..9c046149 --- /dev/null +++ b/test/mock/clockmock/clock.go @@ -0,0 +1,13 @@ +package clockmock + +import "time" + +type Impl struct{} + +func New() *Impl { + return &Impl{} +} + +func (c *Impl) Now() time.Time { + return time.Time{} +} diff --git a/test/mock/gitmock/git.go b/test/mock/gitmock/git.go new file mode 100755 index 00000000..4938fb6e --- /dev/null +++ b/test/mock/gitmock/git.go @@ -0,0 +1,117 @@ +package gitmock + +import ( + "context" + "fmt" + "io" + "net/url" + "os" + "path/filepath" + "strings" + + "github.com/go-git/go-billy/v5" + "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-billy/v5/osfs" + "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/storage/memory" +) + +type Impl struct { + currentRef string + refsFs map[string]billy.Filesystem +} + +func New() *Impl { + return &Impl{ + refsFs: make(map[string]billy.Filesystem), + } +} + +func (c *Impl) SetupFilesystem() error { + root := "../resources/git-mocks" + rootFileSystem := osfs.New(root) + files, err := rootFileSystem.ReadDir("") + if err != nil { + return err + } + + for _, file := range files { + filePath := filepath.Join(root, strings.ReplaceAll(file.Name(), "%", "%%")) + if file.IsDir() { + origin := osfs.New(fmt.Sprintf(filePath)) + fileSystem, innerErr := c.copyToMemory(origin) + if innerErr != nil { + return err + } + reference, innerErr := url.QueryUnescape(file.Name()) + if innerErr != nil { + return innerErr + } + c.refsFs[reference] = fileSystem + } + } + return nil +} + +func (c *Impl) CloneCommit( + _ context.Context, + gitURL string, + gitReference string, + targetPath string, +) (*git.Repository, error) { + c.currentRef = gitReference + fsRef, ok := c.refsFs[c.currentRef] + if !ok { + return nil, fmt.Errorf("ToDo") + } + return git.Init(memory.NewStorage(), fsRef) +} + +func (c *Impl) copyToMemory(origin billy.Filesystem) (billy.Filesystem, error) { + var copyRecursively func(origin, memory billy.Filesystem, currentPath string) error + copyRecursively = func(origin, memory billy.Filesystem, currentPath string) error { + files, err := origin.ReadDir(currentPath) + if err != nil { + return err + } + + for _, file := range files { + fileName := filepath.Join(currentPath, file.Name()) + if file.IsDir() { + if innerErr := memory.MkdirAll(fileName, os.ModePerm); innerErr != nil { + return innerErr + } + if innerErr := copyRecursively(origin, memory, fileName); innerErr != nil { + return innerErr + } + } else { + src, innerErr := origin.Open(fileName) + if innerErr != nil { + return innerErr + } + + dst, innerErr := memory.Create(fileName) + if innerErr != nil { + return innerErr + } + + if _, innerErr = io.Copy(dst, src); innerErr != nil { + return innerErr + } + if innerErr = dst.Close(); innerErr != nil { + return innerErr + } + if innerErr = src.Close(); innerErr != nil { + return innerErr + } + } + } + return nil + } + + newFs := memfs.New() + if err := copyRecursively(origin, newFs, ""); err != nil { + return nil, err + } + return newFs, nil +} diff --git a/test/mock/helmremotemock/helmremote.go b/test/mock/helmremotemock/helmremote.go new file mode 100644 index 00000000..c080c140 --- /dev/null +++ b/test/mock/helmremotemock/helmremote.go @@ -0,0 +1,31 @@ +package helmremotemock + +import ( + "context" + + "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/repo" +) + +type Impl struct { +} + +func New() *Impl { + return &Impl{} +} + +func (r *Impl) GetIndex( + _ context.Context, + repositoryURL string, + providers []getter.Provider, +) (*repo.IndexFile, error) { + panic("ToDo: implement") +} + +func (r *Impl) RetrieveChart( + _ context.Context, + chartRef string, + providers []getter.Provider, +) ([]byte, error) { + panic("ToDo: implement") +} diff --git a/test/resources/acceptance/acceptance-config.yaml b/test/resources/acceptance/acceptance-config.yaml new file mode 100644 index 00000000..af6fa0c0 --- /dev/null +++ b/test/resources/acceptance/acceptance-config.yaml @@ -0,0 +1,12 @@ +APPLICATION_NAME: manifest-maestro + +LOG_STYLE: PLAIN + +WORKING_DIRECTORY: /tmp/manifest-maestro/workdir + +VAULT_ENABLED: false + +LOG_ATTRIBUTE_KEY_MAPPINGS: >- + { + "service-name": "service.name" + } diff --git a/test/resources/mocks/git-repositories/test/.helmignore b/test/resources/mocks/git-repositories/test/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/test/resources/mocks/git-repositories/test/Chart.yaml b/test/resources/mocks/git-repositories/test/Chart.yaml new file mode 100644 index 00000000..bc53715a --- /dev/null +++ b/test/resources/mocks/git-repositories/test/Chart.yaml @@ -0,0 +1,29 @@ +apiVersion: v2 +name: test +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: 1.16.0 + +dependencies: + - name: base + version: 1.21.1 + repository: test diff --git a/test/resources/mocks/git-repositories/test/README.md b/test/resources/mocks/git-repositories/test/README.md new file mode 100644 index 00000000..e69de29b diff --git a/test/resources/mocks/git-repositories/test/templates/NOTES.txt b/test/resources/mocks/git-repositories/test/templates/NOTES.txt new file mode 100644 index 00000000..5577ecc5 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "test.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "test.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "test.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "test.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/test/resources/mocks/git-repositories/test/templates/_helpers.tpl b/test/resources/mocks/git-repositories/test/templates/_helpers.tpl new file mode 100644 index 00000000..7286a2d8 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "test.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "test.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "test.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "test.labels" -}} +helm.sh/chart: {{ include "test.chart" . }} +{{ include "test.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "test.selectorLabels" -}} +app.kubernetes.io/name: {{ include "test.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "test.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "test.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/test/resources/mocks/git-repositories/test/templates/deployment.yaml b/test/resources/mocks/git-repositories/test/templates/deployment.yaml new file mode 100644 index 00000000..739327e0 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "test.fullname" . }} + labels: + {{- include "test.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "test.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "test.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "test.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/test/resources/mocks/git-repositories/test/templates/hpa.yaml b/test/resources/mocks/git-repositories/test/templates/hpa.yaml new file mode 100644 index 00000000..7afd4c98 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "test.fullname" . }} + labels: + {{- include "test.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "test.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/test/resources/mocks/git-repositories/test/templates/ingress.yaml b/test/resources/mocks/git-repositories/test/templates/ingress.yaml new file mode 100644 index 00000000..62771cf6 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "test.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "test.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/test/resources/mocks/git-repositories/test/templates/service.yaml b/test/resources/mocks/git-repositories/test/templates/service.yaml new file mode 100644 index 00000000..af7828af --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "test.fullname" . }} + labels: + {{- include "test.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "test.selectorLabels" . | nindent 4 }} diff --git a/test/resources/mocks/git-repositories/test/templates/serviceaccount.yaml b/test/resources/mocks/git-repositories/test/templates/serviceaccount.yaml new file mode 100644 index 00000000..0fc75716 --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "test.serviceAccountName" . }} + labels: + {{- include "test.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/test/resources/mocks/git-repositories/test/templates/tests/test-connection.yaml b/test/resources/mocks/git-repositories/test/templates/tests/test-connection.yaml new file mode 100644 index 00000000..f78ec6dc --- /dev/null +++ b/test/resources/mocks/git-repositories/test/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "test.fullname" . }}-test-connection" + labels: + {{- include "test.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "test.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/test/resources/mocks/git-repositories/test/values.yaml b/test/resources/mocks/git-repositories/test/values.yaml new file mode 100644 index 00000000..20d65fcc --- /dev/null +++ b/test/resources/mocks/git-repositories/test/values.yaml @@ -0,0 +1,107 @@ +# Default values for test. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {}