From 267c880474107392b5413cfd6dd4e92babe53699 Mon Sep 17 00:00:00 2001 From: qvalentin Date: Fri, 12 Apr 2024 21:39:29 +0200 Subject: [PATCH] feat: use helm as a libary instead of copying code --- go.mod | 110 +++- go.sum | 354 ++++++++++- internal/charts/chart_for_document.go | 2 +- internal/charts/chart_store_test.go | 2 +- internal/charts/chart_test.go | 2 +- internal/charts/metadata.go | 7 +- internal/charts/values_file.go | 5 +- internal/charts/values_file_test.go | 2 +- internal/handler/completion.go | 2 +- internal/handler/completion_values_test.go | 3 +- internal/handler/hover.go | 6 +- internal/handler/hover_main_test.go | 3 + internal/handler/hover_test.go | 24 +- internal/handler/project_files.go | 4 +- internal/lsp/lint.go | 59 +- internal/lsp/lint_test.go | 14 + internal/util/yaml.go | 12 + pkg/action/lint.go | 106 ---- pkg/chart/chart.go | 173 ------ pkg/chart/dependency.go | 79 --- pkg/chart/errors.go | 30 - pkg/chart/file.go | 27 - pkg/chart/loader/archive.go | 204 ------ pkg/chart/loader/directory.go | 119 ---- pkg/chart/loader/load.go | 201 ------ pkg/chart/metadata.go | 161 ----- pkg/chartutil/capabilities.go | 132 ---- pkg/chartutil/chartfile.go | 92 --- pkg/chartutil/coalesce.go | 227 ------- pkg/chartutil/compatible.go | 36 -- pkg/chartutil/create.go | 686 --------------------- pkg/chartutil/dependencies.go | 285 --------- pkg/chartutil/errors.go | 41 -- pkg/chartutil/expand.go | 90 --- pkg/chartutil/jsonschema.go | 87 --- pkg/chartutil/save.go | 244 -------- pkg/chartutil/validate_name.go | 112 ---- pkg/chartutil/values.go | 255 -------- pkg/engine/engine.go | 416 ------------- pkg/engine/error.go | 25 - pkg/engine/files.go | 160 ----- pkg/engine/files_test.go | 98 --- pkg/engine/funcs.go | 179 ------ pkg/engine/funcs_test.go | 178 ------ pkg/engine/lookup_func.go | 124 ---- pkg/lint/lint.go | 37 -- pkg/lint/rules/chartfile.go | 209 ------- pkg/lint/rules/dependencies.go | 82 --- pkg/lint/rules/deprecations.go | 98 --- pkg/lint/rules/template.go | 385 ------------ pkg/lint/rules/values.go | 86 --- pkg/lint/support/error.go | 1 - pkg/lint/support/message.go | 97 --- testdata/example/templates/lint.yaml | 1 + 54 files changed, 544 insertions(+), 5630 deletions(-) create mode 100644 internal/lsp/lint_test.go delete mode 100644 pkg/action/lint.go delete mode 100644 pkg/chart/chart.go delete mode 100644 pkg/chart/dependency.go delete mode 100644 pkg/chart/errors.go delete mode 100644 pkg/chart/file.go delete mode 100644 pkg/chart/loader/archive.go delete mode 100644 pkg/chart/loader/directory.go delete mode 100644 pkg/chart/loader/load.go delete mode 100644 pkg/chart/metadata.go delete mode 100644 pkg/chartutil/capabilities.go delete mode 100644 pkg/chartutil/chartfile.go delete mode 100644 pkg/chartutil/coalesce.go delete mode 100644 pkg/chartutil/compatible.go delete mode 100644 pkg/chartutil/create.go delete mode 100644 pkg/chartutil/dependencies.go delete mode 100644 pkg/chartutil/errors.go delete mode 100644 pkg/chartutil/expand.go delete mode 100644 pkg/chartutil/jsonschema.go delete mode 100644 pkg/chartutil/save.go delete mode 100644 pkg/chartutil/validate_name.go delete mode 100644 pkg/chartutil/values.go delete mode 100644 pkg/engine/engine.go delete mode 100644 pkg/engine/error.go delete mode 100644 pkg/engine/files.go delete mode 100644 pkg/engine/files_test.go delete mode 100644 pkg/engine/funcs.go delete mode 100644 pkg/engine/funcs_test.go delete mode 100644 pkg/engine/lookup_func.go delete mode 100644 pkg/lint/lint.go delete mode 100644 pkg/lint/rules/chartfile.go delete mode 100644 pkg/lint/rules/dependencies.go delete mode 100644 pkg/lint/rules/deprecations.go delete mode 100644 pkg/lint/rules/template.go delete mode 100644 pkg/lint/rules/values.go delete mode 100644 pkg/lint/support/error.go delete mode 100644 pkg/lint/support/message.go create mode 100644 testdata/example/templates/lint.yaml diff --git a/go.mod b/go.mod index 9b3ad0ea..cf6416e2 100644 --- a/go.mod +++ b/go.mod @@ -3,84 +3,158 @@ module github.com/mrjosh/helm-ls go 1.21 require ( - github.com/BurntSushi/toml v1.3.2 - github.com/Masterminds/semver/v3 v3.2.1 - github.com/Masterminds/sprig/v3 v3.2.3 - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 - github.com/cyphar/filepath-securejoin v0.2.4 - github.com/gobwas/glob v0.2.3 - github.com/mitchellh/copystructure v1.2.0 github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560 github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.9.0 - github.com/xeipuuv/gojsonschema v1.2.0 go.lsp.dev/jsonrpc2 v0.10.0 go.lsp.dev/protocol v0.12.0 go.lsp.dev/uri v0.3.0 go.uber.org/zap v1.24.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/apiextensions-apiserver v0.29.2 - k8s.io/apimachinery v0.29.2 - k8s.io/apiserver v0.29.2 - k8s.io/client-go v0.29.2 - k8s.io/helm v2.17.0+incompatible - sigs.k8s.io/yaml v1.4.0 + helm.sh/helm/v3 v3.14.4 ) require ( + 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/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Masterminds/squirrel v1.5.4 // indirect + github.com/Microsoft/hcsshim v0.11.4 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chai2010/gettext-go v1.0.2 // indirect + github.com/containerd/containerd v1.7.12 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/docker/cli v24.0.6+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker v24.0.9+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect + github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-metrics v0.0.1 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.7.0+incompatible // indirect + github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect github.com/frankban/quicktest v1.14.4 // indirect + github.com/go-errors/errors v1.4.2 // indirect + github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-logr/logr v1.3.0 // 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.3 // indirect + github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // 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/google/uuid v1.3.0 // indirect - github.com/huandu/xstrings v1.3.3 // indirect - github.com/imdario/mergo v0.3.11 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/gosuri/uitable v0.0.4 // indirect + github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/huandu/xstrings v1.4.0 // indirect + github.com/imdario/mergo v0.3.13 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jmoiron/sqlx v1.3.5 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect + github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect + github.com/lib/pq v1.10.9 // indirect + github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/spdystream v0.2.0 // 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/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc5 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.44.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/rubenv/sql-migrate v1.5.2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/segmentio/asm v1.1.3 // indirect github.com/segmentio/encoding v0.3.4 // indirect - github.com/shopspring/decimal v1.2.0 // indirect + github.com/shopspring/decimal v1.3.1 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // 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.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect go.uber.org/atomic v1.10.0 // indirect go.uber.org/goleak v1.2.1 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.17.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect + golang.org/x/sync v0.5.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/grpc v1.58.3 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/api v0.29.2 // indirect + k8s.io/apiextensions-apiserver v0.29.2 // indirect + k8s.io/apimachinery v0.29.2 // indirect + k8s.io/apiserver v0.29.2 // indirect + k8s.io/cli-runtime v0.29.0 // indirect + k8s.io/client-go v0.29.2 // indirect + k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/kubectl v0.29.0 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + oras.land/oras-go v1.2.4 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect + sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index ca232526..d648780d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,15 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +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 v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= 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.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= @@ -7,42 +17,158 @@ github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0 github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= +github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8= +github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w= +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/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/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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +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/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/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= +github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +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.12 h1:+KQsnv4VnzyxWcfO9mlxxELaoztsDEjOuCMPAuPqgU0= +github.com/containerd/containerd v1.7.12/go.mod h1:/5OMpE1p0ylxtEUGY8kuCYkDRzJm9NO1TFMWjUpdevk= +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/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/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.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/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/docker/cli v24.0.6+incompatible h1:fF+XCQCgJjjQNIMjzaSmiKJSCcfcXb3TWTcc7GAneOY= +github.com/docker/cli v24.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0= +github.com/docker/docker v24.0.9+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.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +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/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/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/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/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= +github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/foxcpp/go-mockdns v1.0.0 h1:7jBqxd3WDWwi/6WhDvacvH1XsN3rOLXyHM1uhvIx6FI= +github.com/foxcpp/go-mockdns v1.0.0/go.mod h1:lgRN6+KxQBawyIghpnl5CezHFGS9VLzvtVlwxvzXTQ4= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +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-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= +github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= +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.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= github.com/go-logr/logr v1.3.0/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 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= +github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/logger v1.0.6 h1:nnZNpxYo0zx+Aj9RfMPBm+x9zAU2OayFh/xrAWi34HU= +github.com/gobuffalo/logger v1.0.6/go.mod h1:J31TBEHR1QLV2683OXTAItYIg8pv2JMHnF/quuAbMjs= +github.com/gobuffalo/packd v1.0.1 h1:U2wXfRr4E9DH8IdsDLlRFwTZTK7hLfq9qT/QHXGVe/0= +github.com/gobuffalo/packd v1.0.1/go.mod h1:PP2POP3p3RXGz7Jh6eYEf93S7vA2za6xM7QT85L4+VY= +github.com/gobuffalo/packr/v2 v2.8.3 h1:xE1yzvnO56cUC0sTpKR3DIbxZgB54AftTFMhB2XEWlY= +github.com/gobuffalo/packr/v2 v2.8.3/go.mod h1:0SahksCVcx4IMnigTjiFuyldmTrdTctXsOdiU5KwbKc= 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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +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.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= @@ -52,21 +178,54 @@ 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-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +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.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +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/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= +github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +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/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +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.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= +github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= +github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= +github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= 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/karrick/godirwalk v1.16.1 h1:DynhcF+bztK8gooS0+NDJFrdNZjJ3gzVzC545UNA9iw= +github.com/karrick/godirwalk v1.16.1/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= 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.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +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.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= @@ -74,40 +233,129 @@ 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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= +github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= +github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +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/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= +github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= +github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= +github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= +github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= +github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= +github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.1.25 h1:dFwPR6SfLtrSwgDcIq2bcU/gVutB4sNApq2HBdqcakg= +github.com/miekg/dns v1.1.25/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= 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/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +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/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= +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/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/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 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/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +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-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI= +github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +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/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= +github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= +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.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +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.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +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.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= +github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= +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.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rubenv/sql-migrate v1.5.2 h1:bMDqOnrJVV/6JQgQ/MxOpU+AdO8uzYYA/TxFUBzFtS0= +github.com/rubenv/sql-migrate v1.5.2/go.mod h1:H38GW8Vqf8F0Su5XignRyaRcbXbJunSWxs+kmzlg0Is= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc= github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg= github.com/segmentio/encoding v0.3.4 h1:WM4IBnxH8B9TakiM2QD5LyNl9JSndh88QbHqVC+Pauc= github.com/segmentio/encoding v0.3.4/go.mod h1:n0JeuIqEQrQoPDGsjo8UNd1iA0U8d8+oHAA4E3G3OxM= -github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smacker/go-tree-sitter v0.0.0-20240214120134-1f283e24f560 h1:i1kygzBpj4bIXk+ztDJCnmywZrbpsRJG1QCMrMI0P3o= @@ -120,6 +368,7 @@ github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyh 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= @@ -134,15 +383,24 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI= go.lsp.dev/jsonrpc2 v0.10.0/go.mod h1:fmEzIdXPi/rf6d4uFcayi8HpFP1nBF99ERP1htC72Ac= go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 h1:hCzQgh6UcwbKgNSRurYWSqh8MufqRRPODRBblutn4TE= @@ -151,6 +409,18 @@ go.lsp.dev/protocol v0.12.0 h1:tNprUI9klQW5FAFVM4Sa+AbPFuVQByWhP1ttNUAjIWg= go.lsp.dev/protocol v0.12.0/go.mod h1:Qb11/HgZQ72qQbeyPfJbu3hZBH23s1sr4st8czGeDMQ= go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo= go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I= +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.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= +go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= @@ -159,6 +429,7 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +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= @@ -166,11 +437,23 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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= @@ -179,26 +462,42 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= @@ -212,6 +511,10 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 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= @@ -222,25 +525,54 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.58.3 h1:BjnpXut1btbtgN/6sp+brB2Kbm2LjNXnidYujAVbSoQ= +google.golang.org/grpc v1.58.3/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/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-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= +gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= +helm.sh/helm/v3 v3.14.4 h1:6FSpEfqyDalHq3kUr4gOMThhgY55kXUEjdQoyODYnrM= +helm.sh/helm/v3 v3.14.4/go.mod h1:Tje7LL4gprZpuBNTbG34d1Xn5NmRT3OWfBRwpOSer9I= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= k8s.io/apiextensions-apiserver v0.29.2 h1:UK3xB5lOWSnhaCk0RFZ0LUacPZz9RY4wi/yt2Iu+btg= @@ -249,18 +581,28 @@ k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/apiserver v0.29.2 h1:+Z9S0dSNr+CjnVXQePG8TcBWHr3Q7BmAr7NraHvsMiQ= k8s.io/apiserver v0.29.2/go.mod h1:B0LieKVoyU7ykQvPFm7XSdIHaCHSzCzQWPFa5bqbeMQ= +k8s.io/cli-runtime v0.29.0 h1:q2kC3cex4rOBLfPOnMSzV2BIrrQlx97gxHJs21KxKS4= +k8s.io/cli-runtime v0.29.0/go.mod h1:VKudXp3X7wR45L+nER85YUzOQIru28HQpXr0mTdeCrk= k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= -k8s.io/helm v2.17.0+incompatible h1:Bpn6o1wKLYqKM3+Osh8e+1/K2g/GsQJ4F4yNF2+deao= -k8s.io/helm v2.17.0+incompatible/go.mod h1:LZzlS4LQBHfciFOurYBFkCMTaZ0D1l+p0teMg7TSULI= +k8s.io/component-base v0.29.2 h1:lpiLyuvPA9yV1aQwGLENYyK7n/8t6l3nn3zAtFTJYe8= +k8s.io/component-base v0.29.2/go.mod h1:BfB3SLrefbZXiBfbM+2H1dlat21Uewg/5qtKOl8degM= k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kubectl v0.29.0 h1:Oqi48gXjikDhrBF67AYuZRTcJV4lg2l42GmvsP7FmYI= +k8s.io/kubectl v0.29.0/go.mod h1:0jMjGWIcMIQzmUaMgAzhSELv5WtHo2a8pq67DtviAJs= k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +oras.land/oras-go v1.2.4 h1:djpBY2/2Cs1PV87GSJlxv4voajVOMZxqqtq9AB8YNvY= +oras.land/oras-go v1.2.4/go.mod h1:DYcGfb3YF1nKjcezfX2SNlDAeQFKSXmf+qrFmrh4324= 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.13.5-0.20230601165947-6ce0bf390ce3 h1:XX3Ajgzov2RKUdc5jW3t5jwY7Bo7dcRm+tFxT+NfgY0= +sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 h1:W6cLQc5pnqM7vh3b7HvGNfXrJ/xL6BDMS0v1V/HHg5U= +sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= 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= diff --git a/internal/charts/chart_for_document.go b/internal/charts/chart_for_document.go index c6f46915..de7fd68c 100644 --- a/internal/charts/chart_for_document.go +++ b/internal/charts/chart_for_document.go @@ -6,9 +6,9 @@ import ( "path/filepath" "strings" - "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" ) func (s *ChartStore) GetChartForDoc(uri lsp.DocumentURI) (*Chart, error) { diff --git a/internal/charts/chart_store_test.go b/internal/charts/chart_store_test.go index 49646a78..3cd00468 100644 --- a/internal/charts/chart_store_test.go +++ b/internal/charts/chart_store_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chartutil" ) func TestSetValuesFilesConfigOverwrites(t *testing.T) { diff --git a/internal/charts/chart_test.go b/internal/charts/chart_test.go index d9f88998..d2057304 100644 --- a/internal/charts/chart_test.go +++ b/internal/charts/chart_test.go @@ -7,8 +7,8 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chart" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chart" "github.com/stretchr/testify/assert" ) diff --git a/internal/charts/metadata.go b/internal/charts/metadata.go index 53c47ab4..f5dd8e11 100644 --- a/internal/charts/metadata.go +++ b/internal/charts/metadata.go @@ -3,10 +3,11 @@ package charts import ( "path/filepath" - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" + "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" ) type ChartMetadata struct { @@ -17,7 +18,7 @@ type ChartMetadata struct { func NewChartMetadata(rootURI uri.URI) *ChartMetadata { filePath := filepath.Join(rootURI.Filename(), chartutil.ChartfileName) - chartNode, err := chartutil.ReadYamlFileToNode(filePath) + chartNode, err := util.ReadYamlFileToNode(filePath) if err != nil { logger.Error("Error loading Chart.yaml file", rootURI, err) } diff --git a/internal/charts/values_file.go b/internal/charts/values_file.go index 812882dc..a27385eb 100644 --- a/internal/charts/values_file.go +++ b/internal/charts/values_file.go @@ -1,8 +1,9 @@ package charts import ( - "github.com/mrjosh/helm-ls/pkg/chartutil" + "github.com/mrjosh/helm-ls/internal/util" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" "gopkg.in/yaml.v3" ) @@ -37,7 +38,7 @@ func readInValuesFile(filePath string) (chartutil.Values, yaml.Node) { logger.Error("Error loading values file ", filePath, err) } - valueNodes, err := chartutil.ReadYamlFileToNode(filePath) + valueNodes, err := util.ReadYamlFileToNode(filePath) if err != nil { logger.Error("Error loading values file ", filePath, err) } diff --git a/internal/charts/values_file_test.go b/internal/charts/values_file_test.go index 58efbe56..64830f96 100644 --- a/internal/charts/values_file_test.go +++ b/internal/charts/values_file_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" - "github.com/mrjosh/helm-ls/pkg/chartutil" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" + "helm.sh/helm/v3/pkg/chartutil" ) func TestNewValuesFile(t *testing.T) { diff --git a/internal/handler/completion.go b/internal/handler/completion.go index f6a9feaa..15df95d4 100644 --- a/internal/handler/completion.go +++ b/internal/handler/completion.go @@ -10,13 +10,13 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" gotemplate "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" - "github.com/mrjosh/helm-ls/pkg/chartutil" sitter "github.com/smacker/go-tree-sitter" "go.lsp.dev/protocol" lsp "go.lsp.dev/protocol" yaml "gopkg.in/yaml.v2" "github.com/mrjosh/helm-ls/internal/documentation/godocs" + "helm.sh/helm/v3/pkg/chartutil" ) var ( diff --git a/internal/handler/completion_values_test.go b/internal/handler/completion_values_test.go index 9c454cdf..0cd4d19f 100644 --- a/internal/handler/completion_values_test.go +++ b/internal/handler/completion_values_test.go @@ -5,10 +5,11 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" lsplocal "github.com/mrjosh/helm-ls/internal/lsp" - "github.com/mrjosh/helm-ls/pkg/chart" "github.com/stretchr/testify/assert" "go.lsp.dev/protocol" "gopkg.in/yaml.v3" + + "helm.sh/helm/v3/pkg/chart" ) func TestEmptyValues(t *testing.T) { diff --git a/internal/handler/hover.go b/internal/handler/hover.go index 99133a0e..d32f1a16 100644 --- a/internal/handler/hover.go +++ b/internal/handler/hover.go @@ -14,10 +14,10 @@ import ( "github.com/mrjosh/helm-ls/internal/tree-sitter/gotemplate" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chart" + "helm.sh/helm/v3/pkg/chartutil" ) func (h *langHandler) Hover(ctx context.Context, params *lsp.HoverParams) (result *lsp.Hover, err error) { @@ -184,7 +184,7 @@ func (h *langHandler) getTableOrValueForSelector(values chartutil.Values, select if len(selector) > 0 { localValues, err := values.Table(selector) if err != nil { - logger.Debug("values.PathValue(tableName) because of error", err) + logger.Println("values.PathValue(tableName) because of error", err) value, err := values.PathValue(selector) return h.formatToYAML(reflect.Indirect(reflect.ValueOf(value)), selector), err } diff --git a/internal/handler/hover_main_test.go b/internal/handler/hover_main_test.go index 494b03b0..e07ca8aa 100644 --- a/internal/handler/hover_main_test.go +++ b/internal/handler/hover_main_test.go @@ -139,6 +139,9 @@ func TestHoverMain(t *testing.T) { }, }) assert.Equal(t, tt.expectedError, err) + if result == nil { + t.Fatal("Result is nil") + } assert.Equal(t, tt.expected, result.Contents.Value) }) } diff --git a/internal/handler/hover_test.go b/internal/handler/hover_test.go index 38a2499f..cc57d202 100644 --- a/internal/handler/hover_test.go +++ b/internal/handler/hover_test.go @@ -5,9 +5,9 @@ import ( "testing" "github.com/mrjosh/helm-ls/internal/charts" - "github.com/mrjosh/helm-ls/pkg/chart" "github.com/stretchr/testify/assert" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chart" ) func Test_langHandler_getValueHover(t *testing.T) { @@ -234,6 +234,28 @@ value want: `### values.yaml 1.2345 +`, + wantErr: false, + }, + { + name: "Lookup in list", + args: args{ + chart: &charts.Chart{ + ChartMetadata: &charts.ChartMetadata{}, + ValuesFiles: &charts.ValuesFiles{ + MainValuesFile: &charts.ValuesFile{ + Values: map[string]interface{}{ + "key": []interface{}{"hello"}, + }, + URI: "file://tmp/values.yaml", + }, + }, + }, + splittedVar: []string{"key[0]"}, + }, + want: `### values.yaml +hello + `, wantErr: false, }, diff --git a/internal/handler/project_files.go b/internal/handler/project_files.go index 3816c178..7db235df 100644 --- a/internal/handler/project_files.go +++ b/internal/handler/project_files.go @@ -5,9 +5,9 @@ import ( "os" "path/filepath" - "github.com/mrjosh/helm-ls/pkg/chartutil" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" ) type ProjectFiles struct { @@ -19,12 +19,12 @@ type ProjectFiles struct { func (p ProjectFiles) GetValuesFileURI() lsp.DocumentURI { return "file://" + lsp.DocumentURI(p.ValuesFile) } + func (p ProjectFiles) GetChartFileURI() lsp.DocumentURI { return "file://" + lsp.DocumentURI(p.ChartFile) } func NewProjectFiles(rootURI uri.URI, valuesFileName string) ProjectFiles { - if valuesFileName == "" { valuesFileName = chartutil.ValuesfileName } diff --git a/internal/lsp/lint.go b/internal/lsp/lint.go index a297e787..129f5a26 100644 --- a/internal/lsp/lint.go +++ b/internal/lsp/lint.go @@ -8,14 +8,18 @@ import ( "github.com/mrjosh/helm-ls/internal/charts" "github.com/mrjosh/helm-ls/internal/log" "github.com/mrjosh/helm-ls/internal/util" - "github.com/mrjosh/helm-ls/pkg/action" - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/lint/support" + // "github.com/mrjosh/helm-ls/pkg/action" + + // "github.com/mrjosh/helm-ls/pkg/chartutil" + // "github.com/mrjosh/helm-ls/pkg/lint/support" "github.com/pkg/errors" - "github.com/mrjosh/helm-ls/pkg/lint/rules" lsp "go.lsp.dev/protocol" "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/action" + // "helm.sh/helm/v3/pkg/lint/rules" + "helm.sh/helm/v3/pkg/chartutil" + "helm.sh/helm/v3/pkg/lint/support" ) var logger = log.GetLogger() @@ -69,6 +73,7 @@ func GetDiagnostics(uri uri.URI, vals chartutil.Values) []lsp.Diagnostic { } diagnostics = append(diagnostics, *d) } + logger.Println(fmt.Sprintf("helm lint: result for file %s : %v", uri, diagnostics)) return diagnostics } @@ -87,35 +92,35 @@ func GetDiagnosticFromLinterErr(supMsg support.Message) (*lsp.Diagnostic, string severity = lsp.DiagnosticSeverityError - if superr, ok := supMsg.Err.(*rules.YAMLToJSONParseError); ok { - - line = superr.Line - msg = superr.Error() + // if superr, ok := supMsg.Err.(*rules.YAMLToJSONParseError); ok { - } else { - - fileLine := util.BetweenStrings(supMsg.Error(), "(", ")") - fileLineArr := strings.Split(fileLine, ":") - if len(fileLineArr) < 2 { - return nil, filename, errors.Errorf("linter Err contains no position information") - } - lineStr := fileLineArr[1] - line, err = strconv.Atoi(lineStr) - if err != nil { - return nil, filename, err - } - msgStr := util.AfterStrings(supMsg.Error(), "):") - msg = strings.TrimSpace(msgStr) + // line = superr.Line + // msg = superr.Error() + // + // } else { + fileLine := util.BetweenStrings(supMsg.Error(), "(", ")") + fileLineArr := strings.Split(fileLine, ":") + if len(fileLineArr) < 2 { + return nil, filename, errors.Errorf("linter Err contains no position information") } + lineStr := fileLineArr[1] + line, err = strconv.Atoi(lineStr) + if err != nil { + return nil, filename, err + } + msgStr := util.AfterStrings(supMsg.Error(), "):") + msg = strings.TrimSpace(msgStr) + + // } case support.WarningSev: - severity = lsp.DiagnosticSeverityWarning - if err, ok := supMsg.Err.(*rules.MetadataError); ok { - line = 1 - msg = err.Details().Error() - } + // severity = lsp.DiagnosticSeverityWarning + // if err, ok := supMsg.Err.(*rules.MetadataError); ok { + // line = 1 + // msg = err.Details().Error() + // } case support.InfoSev: diff --git a/internal/lsp/lint_test.go b/internal/lsp/lint_test.go new file mode 100644 index 00000000..7db62846 --- /dev/null +++ b/internal/lsp/lint_test.go @@ -0,0 +1,14 @@ +package lsp + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.lsp.dev/uri" + "helm.sh/helm/v3/pkg/chartutil" +) + +func TestLint(t *testing.T) { + diagnostics := GetDiagnostics(uri.File("../../testdata/example/templates/lint.yaml"), chartutil.Values{}) + assert.NotEmpty(t, diagnostics) +} diff --git a/internal/util/yaml.go b/internal/util/yaml.go index 66f758ef..d980de1d 100644 --- a/internal/util/yaml.go +++ b/internal/util/yaml.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "os" "strings" lsp "go.lsp.dev/protocol" @@ -44,3 +45,14 @@ func GetPositionOfNode(node *yamlv3.Node, query []string) (lsp.Position, error) } return lsp.Position{}, fmt.Errorf("could not find Position of %s in values.yaml. Found no match", query) } + +// ReadYamlFileToNode will parse a YAML file into a yaml Node. +func ReadYamlFileToNode(filename string) (node yamlv3.Node, err error) { + data, err := os.ReadFile(filename) + if err != nil { + return yamlv3.Node{}, err + } + + err = yamlv3.Unmarshal(data, &node) + return node, err +} diff --git a/pkg/action/lint.go b/pkg/action/lint.go deleted file mode 100644 index c14eae7b..00000000 --- a/pkg/action/lint.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package action - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/lint" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Lint is the action for checking that the semantics of a chart are well-formed. -// -// It provides the implementation of 'helm lint'. -type Lint struct { - Strict bool - Namespace string - WithSubcharts bool - Quiet bool -} - -// LintResult is the result of Lint -type LintResult struct { - TotalChartsLinted int - Messages []support.Message - Errors []error -} - -// NewLint creates a new Lint object with the given configuration. -func NewLint() *Lint { - return &Lint{} -} - -// Run executes 'helm Lint' against the given chart. -func (l *Lint) Run(paths []string, vals map[string]interface{}) *LintResult { - - lowestTolerance := support.ErrorSev - if l.Strict { - lowestTolerance = support.WarningSev - } - - result := &LintResult{} - for _, path := range paths { - - linter, err := lintChart(path, vals, l.Namespace, l.Strict) - if err != nil { - result.Errors = append(result.Errors, err) - continue - } - - result.Messages = append(result.Messages, linter.Messages...) - result.TotalChartsLinted++ - for _, msg := range linter.Messages { - if msg.Severity >= lowestTolerance { - result.Errors = append(result.Errors, msg.Err) - } - } - - } - - return result -} - -// HasWaringsOrErrors checks is LintResult has any warnings or errors -func HasWarningsOrErrors(result *LintResult) bool { - for _, msg := range result.Messages { - if msg.Severity > support.InfoSev { - return true - } - } - return false -} - -func lintChart( - path string, - vals map[string]interface{}, - namespace string, - strict bool, -) (support.Linter, error) { - - linter := support.Linter{} - - // Guard: Error out if this is not a chart. - if _, err := os.Stat(filepath.Join(path, "Chart.yaml")); err != nil { - return linter, errors.Wrap(err, "unable to check Chart.yaml file in chart") - } - - return lint.All(path, vals, namespace, strict), nil -} diff --git a/pkg/chart/chart.go b/pkg/chart/chart.go deleted file mode 100644 index a3bed63a..00000000 --- a/pkg/chart/chart.go +++ /dev/null @@ -1,173 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import ( - "path/filepath" - "regexp" - "strings" -) - -// APIVersionV1 is the API version number for version 1. -const APIVersionV1 = "v1" - -// APIVersionV2 is the API version number for version 2. -const APIVersionV2 = "v2" - -// aliasNameFormat defines the characters that are legal in an alias name. -var aliasNameFormat = regexp.MustCompile("^[a-zA-Z0-9_-]+$") - -// Chart is a helm package that contains metadata, a default config, zero or more -// optionally parameterizable templates, and zero or more charts (dependencies). -type Chart struct { - // Raw contains the raw contents of the files originally contained in the chart archive. - // - // This should not be used except in special cases like `helm show values`, - // where we want to display the raw values, comments and all. - Raw []*File `json:"-"` - // Metadata is the contents of the Chartfile. - Metadata *Metadata `json:"metadata"` - // Lock is the contents of Chart.lock. - Lock *Lock `json:"lock"` - // Templates for this chart. - Templates []*File `json:"templates"` - // Values are default config for this chart. - Values map[string]interface{} `json:"values"` - // Schema is an optional JSON schema for imposing structure on Values - Schema []byte `json:"schema"` - // Files are miscellaneous files in a chart archive, - // e.g. README, LICENSE, etc. - Files []*File `json:"files"` - - parent *Chart - dependencies []*Chart -} - -type CRD struct { - // Name is the File.Name for the crd file - Name string - // Filename is the File obj Name including (sub-)chart.ChartFullPath - Filename string - // File is the File obj for the crd - File *File -} - -// SetDependencies replaces the chart dependencies. -func (ch *Chart) SetDependencies(charts ...*Chart) { - ch.dependencies = nil - ch.AddDependency(charts...) -} - -// Name returns the name of the chart. -func (ch *Chart) Name() string { - if ch.Metadata == nil { - return "" - } - return ch.Metadata.Name -} - -// AddDependency determines if the chart is a subchart. -func (ch *Chart) AddDependency(charts ...*Chart) { - for i, x := range charts { - charts[i].parent = ch - ch.dependencies = append(ch.dependencies, x) - } -} - -// Root finds the root chart. -func (ch *Chart) Root() *Chart { - if ch.IsRoot() { - return ch - } - return ch.Parent().Root() -} - -// Dependencies are the charts that this chart depends on. -func (ch *Chart) Dependencies() []*Chart { return ch.dependencies } - -// IsRoot determines if the chart is the root chart. -func (ch *Chart) IsRoot() bool { return ch.parent == nil } - -// Parent returns a subchart's parent chart. -func (ch *Chart) Parent() *Chart { return ch.parent } - -// ChartPath returns the full path to this chart in dot notation. -func (ch *Chart) ChartPath() string { - if !ch.IsRoot() { - return ch.Parent().ChartPath() + "." + ch.Name() - } - return ch.Name() -} - -// ChartFullPath returns the full path to this chart. -func (ch *Chart) ChartFullPath() string { - if !ch.IsRoot() { - return ch.Parent().ChartFullPath() + "/charts/" + ch.Name() - } - return ch.Name() -} - -// Validate validates the metadata. -func (ch *Chart) Validate() error { - return ch.Metadata.Validate() -} - -// AppVersion returns the appversion of the chart. -func (ch *Chart) AppVersion() string { - if ch.Metadata == nil { - return "" - } - return ch.Metadata.AppVersion -} - -// CRDs returns a list of File objects in the 'crds/' directory of a Helm chart. -// Deprecated: use CRDObjects() -func (ch *Chart) CRDs() []*File { - files := []*File{} - // Find all resources in the crds/ directory - for _, f := range ch.Files { - if strings.HasPrefix(f.Name, "crds/") && hasManifestExtension(f.Name) { - files = append(files, f) - } - } - // Get CRDs from dependencies, too. - for _, dep := range ch.Dependencies() { - files = append(files, dep.CRDs()...) - } - return files -} - -// CRDObjects returns a list of CRD objects in the 'crds/' directory of a Helm chart & subcharts -func (ch *Chart) CRDObjects() []CRD { - crds := []CRD{} - // Find all resources in the crds/ directory - for _, f := range ch.Files { - if strings.HasPrefix(f.Name, "crds/") && hasManifestExtension(f.Name) { - mycrd := CRD{Name: f.Name, Filename: filepath.Join(ch.ChartFullPath(), f.Name), File: f} - crds = append(crds, mycrd) - } - } - // Get CRDs from dependencies, too. - for _, dep := range ch.Dependencies() { - crds = append(crds, dep.CRDObjects()...) - } - return crds -} - -func hasManifestExtension(fname string) bool { - ext := filepath.Ext(fname) - return strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml") || strings.EqualFold(ext, ".json") -} diff --git a/pkg/chart/dependency.go b/pkg/chart/dependency.go deleted file mode 100644 index b2819f37..00000000 --- a/pkg/chart/dependency.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import "time" - -// Dependency describes a chart upon which another chart depends. -// -// Dependencies can be used to express developer intent, or to capture the state -// of a chart. -type Dependency struct { - // Name is the name of the dependency. - // - // This must mach the name in the dependency's Chart.yaml. - Name string `json:"name"` - // Version is the version (range) of this chart. - // - // A lock file will always produce a single version, while a dependency - // may contain a semantic version range. - Version string `json:"version,omitempty"` - // The URL to the repository. - // - // Appending `index.yaml` to this string should result in a URL that can be - // used to fetch the repository index. - Repository string `json:"repository"` - // A yaml path that resolves to a boolean, used for enabling/disabling charts (e.g. subchart1.enabled ) - Condition string `json:"condition,omitempty"` - // Tags can be used to group charts for enabling/disabling together - Tags []string `json:"tags,omitempty"` - // Enabled bool determines if chart should be loaded - Enabled bool `json:"enabled,omitempty"` - // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a - // string or pair of child/parent sublist items. - ImportValues []interface{} `json:"import-values,omitempty"` - // Alias usable alias to be used for the chart - Alias string `json:"alias,omitempty"` -} - -// Validate checks for common problems with the dependency datastructure in -// the chart. This check must be done at load time before the dependency's charts are -// loaded. -func (d *Dependency) Validate() error { - d.Name = sanitizeString(d.Name) - d.Version = sanitizeString(d.Version) - d.Repository = sanitizeString(d.Repository) - d.Condition = sanitizeString(d.Condition) - for i := range d.Tags { - d.Tags[i] = sanitizeString(d.Tags[i]) - } - if d.Alias != "" && !aliasNameFormat.MatchString(d.Alias) { - return ValidationErrorf("dependency %q has disallowed characters in the alias", d.Name) - } - return nil -} - -// Lock is a lock file for dependencies. -// -// It represents the state that the dependencies should be in. -type Lock struct { - // Generated is the date the lock file was last generated. - Generated time.Time `json:"generated"` - // Digest is a hash of the dependencies in Chart.yaml. - Digest string `json:"digest"` - // Dependencies is the list of dependencies that this lock file has locked. - Dependencies []*Dependency `json:"dependencies"` -} diff --git a/pkg/chart/errors.go b/pkg/chart/errors.go deleted file mode 100644 index 2fad5f37..00000000 --- a/pkg/chart/errors.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import "fmt" - -// ValidationError represents a data validation error. -type ValidationError string - -func (v ValidationError) Error() string { - return "validation: " + string(v) -} - -// ValidationErrorf takes a message and formatting options and creates a ValidationError -func ValidationErrorf(msg string, args ...interface{}) ValidationError { - return ValidationError(fmt.Sprintf(msg, args...)) -} diff --git a/pkg/chart/file.go b/pkg/chart/file.go deleted file mode 100644 index 9dd7c08d..00000000 --- a/pkg/chart/file.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -// File represents a file as a name/value pair. -// -// By convention, name is a relative path within the scope of the chart's -// base directory. -type File struct { - // Name is the path-like name of the template. - Name string `json:"name"` - // Data is the template as byte data. - Data []byte `json:"data"` -} diff --git a/pkg/chart/loader/archive.go b/pkg/chart/loader/archive.go deleted file mode 100644 index d9e9a52b..00000000 --- a/pkg/chart/loader/archive.go +++ /dev/null @@ -1,204 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "archive/tar" - "bytes" - "compress/gzip" - "fmt" - "io" - "net/http" - "os" - "path" - "regexp" - "strings" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -var drivePathPattern = regexp.MustCompile(`^[a-zA-Z]:/`) - -const ChartFileName = "Chart.yaml" - -// FileLoader loads a chart from a file -type FileLoader string - -// Load loads a chart -func (l FileLoader) Load() (*chart.Chart, error) { - return LoadFile(string(l)) -} - -// LoadFile loads from an archive file. -func LoadFile(name string) (*chart.Chart, error) { - if fi, err := os.Stat(name); err != nil { - return nil, err - } else if fi.IsDir() { - return nil, errors.New("cannot load a directory") - } - - raw, err := os.Open(name) - if err != nil { - return nil, err - } - defer raw.Close() - - err = ensureArchive(name, raw) - if err != nil { - return nil, err - } - - c, err := LoadArchive(raw) - if err != nil { - if err == gzip.ErrHeader { - return nil, fmt.Errorf("file '%s' does not appear to be a valid chart file (details: %s)", name, err) - } - } - return c, err -} - -// ensureArchive's job is to return an informative error if the file does not appear to be a gzipped archive. -// -// Sometimes users will provide a values.yaml for an argument where a chart is expected. One common occurrence -// of this is invoking `helm template values.yaml mychart` which would otherwise produce a confusing error -// if we didn't check for this. -func ensureArchive(name string, raw *os.File) error { - - defer func() { - if _, err := raw.Seek(0, 0); err != nil { - panic(err) - } - // reset read offset to allow archive loading to proceed. - }() - - // Check the file format to give us a chance to provide the user with more actionable feedback. - buffer := make([]byte, 512) - _, err := raw.Read(buffer) - if err != nil && err != io.EOF { - return fmt.Errorf("file '%s' cannot be read: %s", name, err) - } - if contentType := http.DetectContentType(buffer); contentType != "application/x-gzip" { - // TODO: Is there a way to reliably test if a file content is YAML? ghodss/yaml accepts a wide - // variety of content (Makefile, .zshrc) as valid YAML without errors. - - // Wrong content type. Let's check if it's yaml and give an extra hint? - if strings.HasSuffix(name, ".yml") || strings.HasSuffix(name, ".yaml") { - return fmt.Errorf("file '%s' seems to be a YAML file, but expected a gzipped archive", name) - } - return fmt.Errorf("file '%s' does not appear to be a gzipped archive; got '%s'", name, contentType) - } - return nil -} - -// LoadArchiveFiles reads in files out of an archive into memory. This function -// performs important path security checks and should always be used before -// expanding a tarball -func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) { - unzipped, err := gzip.NewReader(in) - if err != nil { - return nil, err - } - defer unzipped.Close() - - files := []*BufferedFile{} - tr := tar.NewReader(unzipped) - for { - b := bytes.NewBuffer(nil) - hd, err := tr.Next() - if err == io.EOF { - break - } - if err != nil { - return nil, err - } - - if hd.FileInfo().IsDir() { - // Use this instead of hd.Typeflag because we don't have to do any - // inference chasing. - continue - } - - switch hd.Typeflag { - // We don't want to process these extension header files. - case tar.TypeXGlobalHeader, tar.TypeXHeader: - continue - } - - // Archive could contain \ if generated on Windows - delimiter := "/" - if strings.ContainsRune(hd.Name, '\\') { - delimiter = "\\" - } - - parts := strings.Split(hd.Name, delimiter) - n := strings.Join(parts[1:], delimiter) - - // Normalize the path to the / delimiter - n = strings.ReplaceAll(n, delimiter, "/") - - if path.IsAbs(n) { - return nil, errors.New("chart illegally contains absolute paths") - } - - n = path.Clean(n) - if n == "." { - // In this case, the original path was relative when it should have been absolute. - return nil, errors.Errorf("chart illegally contains content outside the base directory: %q", hd.Name) - } - if strings.HasPrefix(n, "..") { - return nil, errors.New("chart illegally references parent directory") - } - - // In some particularly arcane acts of path creativity, it is possible to intermix - // UNIX and Windows style paths in such a way that you produce a result of the form - // c:/foo even after all the built-in absolute path checks. So we explicitly check - // for this condition. - if drivePathPattern.MatchString(n) { - return nil, errors.New("chart contains illegally named files") - } - - if parts[0] == ChartFileName { - return nil, errors.New("chart yaml not in base directory") - } - - if _, err := io.Copy(b, tr); err != nil { - return nil, err - } - - data := bytes.TrimPrefix(b.Bytes(), utf8bom) - - files = append(files, &BufferedFile{Name: n, Data: data}) - b.Reset() - } - - if len(files) == 0 { - return nil, errors.New("no files in chart archive") - } - return files, nil -} - -// LoadArchive loads from a reader containing a compressed tar archive. -func LoadArchive(in io.Reader) (*chart.Chart, error) { - files, err := LoadArchiveFiles(in) - if err != nil { - return nil, err - } - - return LoadFiles(files) -} diff --git a/pkg/chart/loader/directory.go b/pkg/chart/loader/directory.go deleted file mode 100644 index 552b28cc..00000000 --- a/pkg/chart/loader/directory.go +++ /dev/null @@ -1,119 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "bytes" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "k8s.io/helm/pkg/ignore" - "k8s.io/helm/pkg/sympath" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -var utf8bom = []byte{0xEF, 0xBB, 0xBF} - -// DirLoader loads a chart from a directory -type DirLoader string - -// Load loads the chart -func (l DirLoader) Load() (*chart.Chart, error) { - return LoadDir(string(l)) -} - -// LoadDir loads from a directory. -// -// This loads charts only from directories. -func LoadDir(dir string) (*chart.Chart, error) { - topdir, err := filepath.Abs(dir) - if err != nil { - return nil, err - } - - // Just used for errors. - c := &chart.Chart{} - - rules := ignore.Empty() - ifile := filepath.Join(topdir, ignore.HelmIgnore) - if _, err := os.Stat(ifile); err == nil { - r, err := ignore.ParseFile(ifile) - if err != nil { - return c, err - } - rules = r - } - rules.AddDefaults() - - files := []*BufferedFile{} - topdir += string(filepath.Separator) - - walk := func(name string, fi os.FileInfo, err error) error { - n := strings.TrimPrefix(name, topdir) - if n == "" { - // No need to process top level. Avoid bug with helmignore .* matching - // empty names. See issue 1779. - return nil - } - - // Normalize to / since it will also work on Windows - n = filepath.ToSlash(n) - - if err != nil { - return err - } - if fi.IsDir() { - // Directory-based ignore rules should involve skipping the entire - // contents of that directory. - if rules.Ignore(n, fi) { - return filepath.SkipDir - } - return nil - } - - // If a .helmignore file matches, skip this file. - if rules.Ignore(n, fi) { - return nil - } - - // Irregular files include devices, sockets, and other uses of files that - // are not regular files. In Go they have a file mode type bit set. - // See https://golang.org/pkg/os/#FileMode for examples. - if !fi.Mode().IsRegular() { - return fmt.Errorf("cannot load irregular file %s as it has file mode type bits set", name) - } - - data, err := os.ReadFile(name) - if err != nil { - return errors.Wrapf(err, "error reading %s", n) - } - - data = bytes.TrimPrefix(data, utf8bom) - - files = append(files, &BufferedFile{Name: n, Data: data}) - return nil - } - if err = sympath.Walk(topdir, walk); err != nil { - return c, err - } - - return LoadFiles(files) -} diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go deleted file mode 100644 index 7e177fcd..00000000 --- a/pkg/chart/loader/load.go +++ /dev/null @@ -1,201 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package loader - -import ( - "bytes" - "log" - "os" - "path/filepath" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// ChartLoader loads a chart. -type ChartLoader interface { - Load() (*chart.Chart, error) -} - -// Loader returns a new ChartLoader appropriate for the given chart name -func Loader(name string) (ChartLoader, error) { - fi, err := os.Stat(name) - if err != nil { - return nil, err - } - if fi.IsDir() { - return DirLoader(name), nil - } - return FileLoader(name), nil - -} - -// Load takes a string name, tries to resolve it to a file or directory, and then loads it. -// -// This is the preferred way to load a chart. It will discover the chart encoding -// and hand off to the appropriate chart reader. -// -// If a .helmignore file is present, the directory loader will skip loading any files -// matching it. But .helmignore is not evaluated when reading out of an archive. -func Load(name string) (*chart.Chart, error) { - l, err := Loader(name) - if err != nil { - return nil, err - } - return l.Load() -} - -// BufferedFile represents an archive file buffered for later processing. -type BufferedFile struct { - Name string - Data []byte -} - -// LoadFiles loads from in-memory files. -//nolint -func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { - c := new(chart.Chart) - subcharts := make(map[string][]*BufferedFile) - - // do not rely on assumed ordering of files in the chart and crash - // if Chart.yaml was not coming early enough to initialize metadata - for _, f := range files { - c.Raw = append(c.Raw, &chart.File{Name: f.Name, Data: f.Data}) - if f.Name == "Chart.yaml" { - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { - return c, errors.Wrap(err, "cannot load Chart.yaml") - } - // NOTE(bacongobbler): while the chart specification says that APIVersion must be set, - // Helm 2 accepted charts that did not provide an APIVersion in their chart metadata. - // Because of that, if APIVersion is unset, we should assume we're loading a v1 chart. - if c.Metadata.APIVersion == "" { - c.Metadata.APIVersion = chart.APIVersionV1 - } - } - } - for _, f := range files { - switch { - case f.Name == "Chart.yaml": - // already processed - continue - case f.Name == "Chart.lock": - c.Lock = new(chart.Lock) - if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { - return c, errors.Wrap(err, "cannot load Chart.lock") - } - case f.Name == "values.yaml": - c.Values = make(map[string]interface{}) - if err := yaml.Unmarshal(f.Data, &c.Values); err != nil { - return c, errors.Wrap(err, "cannot load values.yaml") - } - case f.Name == "values.schema.json": - c.Schema = f.Data - - // Deprecated: requirements.yaml is deprecated use Chart.yaml. - // We will handle it for you because we are nice people - case f.Name == "requirements.yaml": - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if c.Metadata.APIVersion != chart.APIVersionV1 { - log.Printf("Warning: Dependencies are handled in Chart.yaml since apiVersion \"v2\". We recommend migrating dependencies to Chart.yaml.") - } - if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { - return c, errors.Wrap(err, "cannot load requirements.yaml") - } - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - // Deprecated: requirements.lock is deprecated use Chart.lock. - case f.Name == "requirements.lock": - c.Lock = new(chart.Lock) - if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { - return c, errors.Wrap(err, "cannot load requirements.lock") - } - if c.Metadata == nil { - c.Metadata = new(chart.Metadata) - } - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - - case strings.HasPrefix(f.Name, "templates/"): - c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) - case strings.HasPrefix(f.Name, "charts/"): - if filepath.Ext(f.Name) == ".prov" { - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - continue - } - - fname := strings.TrimPrefix(f.Name, "charts/") - cname := strings.SplitN(fname, "/", 2)[0] - subcharts[cname] = append(subcharts[cname], &BufferedFile{Name: fname, Data: f.Data}) - default: - c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data}) - } - } - - if c.Metadata == nil { - return c, errors.New("Chart.yaml file is missing") - } - - if err := c.Validate(); err != nil { - return c, err - } - - for n, files := range subcharts { - var sc *chart.Chart - var err error - switch { - case strings.IndexAny(n, "_.") == 0: - continue - case filepath.Ext(n) == ".tgz": - file := files[0] - if file.Name != n { - return c, errors.Errorf("error unpacking tar in %s: expected %s, got %s", c.Name(), n, file.Name) - } - // Untar the chart and add to c.Dependencies - sc, err = LoadArchive(bytes.NewBuffer(file.Data)) - default: - // We have to trim the prefix off of every file, and ignore any file - // that is in charts/, but isn't actually a chart. - buff := make([]*BufferedFile, 0, len(files)) - for _, f := range files { - parts := strings.SplitN(f.Name, "/", 2) - if len(parts) < 2 { - continue - } - f.Name = parts[1] - buff = append(buff, f) - } - sc, err = LoadFiles(buff) - } - - if err != nil { - return c, errors.Wrapf(err, "error unpacking %s in %s", n, c.Name()) - } - c.AddDependency(sc) - } - - return c, nil -} diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go deleted file mode 100644 index ebf4019f..00000000 --- a/pkg/chart/metadata.go +++ /dev/null @@ -1,161 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chart - -import ( - "strings" - //nolint - "unicode" - - semver "github.com/Masterminds/semver/v3" -) - -// Maintainer describes a Chart maintainer. -type Maintainer struct { - // Name is a user name or organization name - Name string `json:"name,omitempty"` - // Email is an optional email address to contact the named maintainer - Email string `json:"email,omitempty"` - // URL is an optional URL to an address for the named maintainer - URL string `json:"url,omitempty"` -} - -// Validate checks valid data and sanitizes string characters. -func (m *Maintainer) Validate() error { - m.Name = sanitizeString(m.Name) - m.Email = sanitizeString(m.Email) - m.URL = sanitizeString(m.URL) - return nil -} - -// Metadata for a Chart file. This models the structure of a Chart.yaml file. -type Metadata struct { - // The name of the chart. Required. - Name string `json:"name,omitempty"` - // The URL to a relevant project page, git repo, or contact person - Home string `json:"home,omitempty"` - // Source is the URL to the source code of this chart - Sources []string `json:"sources,omitempty"` - // A SemVer 2 conformant version string of the chart. Required. - Version string `json:"version,omitempty"` - // A one-sentence description of the chart - Description string `json:"description,omitempty"` - // A list of string keywords - Keywords []string `json:"keywords,omitempty"` - // A list of name and URL/email address combinations for the maintainer(s) - Maintainers []*Maintainer `json:"maintainers,omitempty"` - // The URL to an icon file. - Icon string `json:"icon,omitempty"` - // The API Version of this chart. Required. - APIVersion string `json:"apiVersion,omitempty"` - // The condition to check to enable chart - Condition string `json:"condition,omitempty"` - // The tags to check to enable chart - Tags string `json:"tags,omitempty"` - // The version of the application enclosed inside of this chart. - AppVersion string `json:"appVersion,omitempty"` - // Whether or not this chart is deprecated - Deprecated bool `json:"deprecated,omitempty"` - // Annotations are additional mappings uninterpreted by Helm, - // made available for inspection by other applications. - Annotations map[string]string `json:"annotations,omitempty"` - // KubeVersion is a SemVer constraint specifying the version of Kubernetes required. - KubeVersion string `json:"kubeVersion,omitempty"` - // Dependencies are a list of dependencies for a chart. - Dependencies []*Dependency `json:"dependencies,omitempty"` - // Specifies the chart type: application or library - Type string `json:"type,omitempty"` -} - -// Validate checks the metadata for known issues and sanitizes string -// characters. -func (md *Metadata) Validate() error { - if md == nil { - return ValidationError("chart.metadata is required") - } - - md.Name = sanitizeString(md.Name) - md.Description = sanitizeString(md.Description) - md.Home = sanitizeString(md.Home) - md.Icon = sanitizeString(md.Icon) - md.Condition = sanitizeString(md.Condition) - md.Tags = sanitizeString(md.Tags) - md.AppVersion = sanitizeString(md.AppVersion) - md.KubeVersion = sanitizeString(md.KubeVersion) - for i := range md.Sources { - md.Sources[i] = sanitizeString(md.Sources[i]) - } - for i := range md.Keywords { - md.Keywords[i] = sanitizeString(md.Keywords[i]) - } - - if md.APIVersion == "" { - return ValidationError("chart.metadata.apiVersion is required") - } - if md.Name == "" { - return ValidationError("chart.metadata.name is required") - } - if md.Version == "" { - return ValidationError("chart.metadata.version is required") - } - if !isValidSemver(md.Version) { - return ValidationErrorf("chart.metadata.version %q is invalid", md.Version) - } - if !isValidChartType(md.Type) { - return ValidationError("chart.metadata.type must be application or library") - } - - for _, m := range md.Maintainers { - if err := m.Validate(); err != nil { - return err - } - } - - // Aliases need to be validated here to make sure that the alias name does - // not contain any illegal characters. - for _, dependency := range md.Dependencies { - if err := dependency.Validate(); err != nil { - return err - } - } - return nil -} - -func isValidChartType(in string) bool { - switch in { - case "", "application", "library": - return true - } - return false -} - -func isValidSemver(v string) bool { - _, err := semver.NewVersion(v) - return err == nil -} - -// sanitizeString normalize spaces and removes non-printable characters. -func sanitizeString(str string) string { - return strings.Map(func(r rune) rune { - if unicode.IsSpace(r) { - return ' ' - } - if unicode.IsPrint(r) { - return r - } - return -1 - }, str) -} diff --git a/pkg/chartutil/capabilities.go b/pkg/chartutil/capabilities.go deleted file mode 100644 index 0b37fee5..00000000 --- a/pkg/chartutil/capabilities.go +++ /dev/null @@ -1,132 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "strconv" - - "k8s.io/client-go/kubernetes/scheme" - - apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" - apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" - - semver "github.com/Masterminds/semver/v3" - helmversion "github.com/mrjosh/helm-ls/internal/version" -) - -var ( - // The Kubernetes version can be set by LDFLAGS. In order to do that the value - // must be a string. - k8sVersionMajor = "1" - k8sVersionMinor = "20" - - // DefaultVersionSet is the default version set, which includes only Core V1 ("v1"). - DefaultVersionSet = allKnownVersions() - - // DefaultCapabilities is the default set of capabilities. - DefaultCapabilities = &Capabilities{ - KubeVersion: KubeVersion{ - Version: fmt.Sprintf("v%s.%s.0", k8sVersionMajor, k8sVersionMinor), - Major: k8sVersionMajor, - Minor: k8sVersionMinor, - }, - APIVersions: DefaultVersionSet, - HelmVersion: helmversion.Get(), - } -) - -// Capabilities describes the capabilities of the Kubernetes cluster. -type Capabilities struct { - // KubeVersion is the Kubernetes version. - KubeVersion KubeVersion - // APIversions are supported Kubernetes API versions. - APIVersions VersionSet - // HelmVersion is the build information for this helm version - HelmVersion helmversion.BuildInfo -} - -func (capabilities *Capabilities) Copy() *Capabilities { - return &Capabilities{ - KubeVersion: capabilities.KubeVersion, - APIVersions: capabilities.APIVersions, - HelmVersion: capabilities.HelmVersion, - } -} - -// KubeVersion is the Kubernetes version. -type KubeVersion struct { - Version string // Kubernetes version - Major string // Kubernetes major version - Minor string // Kubernetes minor version -} - -// String implements fmt.Stringer -func (kv *KubeVersion) String() string { return kv.Version } - -// GitVersion returns the Kubernetes version string. -// -// Deprecated: use KubeVersion.Version. -func (kv *KubeVersion) GitVersion() string { return kv.Version } - -// ParseKubeVersion parses kubernetes version from string -func ParseKubeVersion(version string) (*KubeVersion, error) { - sv, err := semver.NewVersion(version) - if err != nil { - return nil, err - } - return &KubeVersion{ - Version: "v" + sv.String(), - Major: strconv.FormatUint(sv.Major(), 10), - Minor: strconv.FormatUint(sv.Minor(), 10), - }, nil -} - -// VersionSet is a set of Kubernetes API versions. -type VersionSet []string - -// Has returns true if the version string is in the set. -// -// vs.Has("apps/v1") -func (v VersionSet) Has(apiVersion string) bool { - for _, x := range v { - if x == apiVersion { - return true - } - } - return false -} - -func allKnownVersions() VersionSet { - - // We should register the built in extension APIs as well so CRDs are - // supported in the default version set. This has caused problems with `helm - // template` in the past, so let's be safe - if err := apiextensionsv1beta1.AddToScheme(scheme.Scheme); err != nil { - panic(err) - } - - if err := apiextensionsv1.AddToScheme(scheme.Scheme); err != nil { - panic(err) - } - - groups := scheme.Scheme.PrioritizedVersionsAllGroups() - vs := make(VersionSet, 0, len(groups)) - for _, gv := range groups { - vs = append(vs, gv.String()) - } - return vs -} diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go deleted file mode 100644 index 07e59ac2..00000000 --- a/pkg/chartutil/chartfile.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// LoadChartfile loads a Chart.yaml file into a *chart.Metadata. -func LoadChartfile(filename string) (*chart.Metadata, error) { - b, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - y := new(chart.Metadata) - err = yaml.Unmarshal(b, y) - return y, err -} - -// SaveChartfile saves the given metadata as a Chart.yaml file at the given path. -// -// 'filename' should be the complete path and filename ('foo/Chart.yaml') -func SaveChartfile(filename string, cf *chart.Metadata) error { - // Pull out the dependencies of a v1 Chart, since there's no way - // to tell the serializer to skip a field for just this use case - savedDependencies := cf.Dependencies - if cf.APIVersion == chart.APIVersionV1 { - cf.Dependencies = nil - } - out, err := yaml.Marshal(cf) - if cf.APIVersion == chart.APIVersionV1 { - cf.Dependencies = savedDependencies - } - if err != nil { - return err - } - return os.WriteFile(filename, out, 0644) -} - -// IsChartDir validate a chart directory. -// -// Checks for a valid Chart.yaml. -func IsChartDir(dirName string) (bool, error) { - if fi, err := os.Stat(dirName); err != nil { - return false, err - } else if !fi.IsDir() { - return false, errors.Errorf("%q is not a directory", dirName) - } - - chartYaml := filepath.Join(dirName, ChartfileName) - if _, err := os.Stat(chartYaml); os.IsNotExist(err) { - return false, errors.Errorf("no %s exists in directory %q", ChartfileName, dirName) - } - - chartYamlContent, err := os.ReadFile(chartYaml) - if err != nil { - return false, errors.Errorf("cannot read %s in directory %q", ChartfileName, dirName) - } - - chartContent := new(chart.Metadata) - if err := yaml.Unmarshal(chartYamlContent, &chartContent); err != nil { - return false, err - } - if chartContent == nil { - return false, errors.Errorf("chart metadata (%s) missing", ChartfileName) - } - if chartContent.Name == "" { - return false, errors.Errorf("invalid chart (%s): name must not be empty", ChartfileName) - } - - return true, nil -} diff --git a/pkg/chartutil/coalesce.go b/pkg/chartutil/coalesce.go deleted file mode 100644 index cbd14244..00000000 --- a/pkg/chartutil/coalesce.go +++ /dev/null @@ -1,227 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "log" - - "github.com/mitchellh/copystructure" - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -func concatPrefix(a, b string) string { - if a == "" { - return b - } - return fmt.Sprintf("%s.%s", a, b) -} - -// CoalesceValues coalesces all of the values in a chart (and its subcharts). -// -// Values are coalesced together using the following rules: -// -// - Values in a higher level chart always override values in a lower-level -// dependency chart -// - Scalar values and arrays are replaced, maps are merged -// - A chart has access to all of the variables for it, as well as all of -// the values destined for its dependencies. -func CoalesceValues(chrt *chart.Chart, vals map[string]interface{}) (Values, error) { - v, err := copystructure.Copy(vals) - if err != nil { - return vals, err - } - - valsCopy := v.(map[string]interface{}) - // if we have an empty map, make sure it is initialized - if valsCopy == nil { - valsCopy = make(map[string]interface{}) - } - return coalesce(log.Printf, chrt, valsCopy, "") -} - -type printFn func(format string, v ...interface{}) - -// coalesce coalesces the dest values and the chart values, giving priority to the dest values. -// -// This is a helper function for CoalesceValues. -func coalesce(printf printFn, ch *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) { - coalesceValues(printf, ch, dest, prefix) - return coalesceDeps(printf, ch, dest, prefix) -} - -// coalesceDeps coalesces the dependencies of the given chart. -func coalesceDeps(printf printFn, chrt *chart.Chart, dest map[string]interface{}, prefix string) (map[string]interface{}, error) { - for _, subchart := range chrt.Dependencies() { - if c, ok := dest[subchart.Name()]; !ok { - // If dest doesn't already have the key, create it. - dest[subchart.Name()] = make(map[string]interface{}) - } else if !istable(c) { - return dest, errors.Errorf("type mismatch on %s: %t", subchart.Name(), c) - } - if dv, ok := dest[subchart.Name()]; ok { - dvmap := dv.(map[string]interface{}) - subPrefix := concatPrefix(prefix, chrt.Metadata.Name) - - // Get globals out of dest and merge them into dvmap. - coalesceGlobals(printf, dvmap, dest, subPrefix) - - // Now coalesce the rest of the values. - var err error - dest[subchart.Name()], err = coalesce(printf, subchart, dvmap, subPrefix) - if err != nil { - return dest, err - } - } - } - return dest, nil -} - -// coalesceGlobals copies the globals out of src and merges them into dest. -// -// For convenience, returns dest. -func coalesceGlobals(printf printFn, dest, src map[string]interface{}, prefix string) { - var dg, sg map[string]interface{} - - if destglob, ok := dest[GlobalKey]; !ok { - dg = make(map[string]interface{}) - } else if dg, ok = destglob.(map[string]interface{}); !ok { - printf("warning: skipping globals because destination %s is not a table.", GlobalKey) - return - } - - if srcglob, ok := src[GlobalKey]; !ok { - sg = make(map[string]interface{}) - } else if sg, ok = srcglob.(map[string]interface{}); !ok { - printf("warning: skipping globals because source %s is not a table.", GlobalKey) - return - } - - // EXPERIMENTAL: In the past, we have disallowed globals to test tables. This - // reverses that decision. It may somehow be possible to introduce a loop - // here, but I haven't found a way. So for the time being, let's allow - // tables in globals. - for key, val := range sg { - if istable(val) { - vv := copyMap(val.(map[string]interface{})) - if destv, ok := dg[key]; !ok { - // Here there is no merge. We're just adding. - dg[key] = vv - } else { - if destvmap, ok := destv.(map[string]interface{}); !ok { - printf("Conflict: cannot merge map onto non-map for %q. Skipping.", key) - } else { - // Basically, we reverse order of coalesce here to merge - // top-down. - subPrefix := concatPrefix(prefix, key) - coalesceTablesFullKey(printf, vv, destvmap, subPrefix) - dg[key] = vv - } - } - } else if dv, ok := dg[key]; ok && istable(dv) { - // It's not clear if this condition can actually ever trigger. - printf("key %s is table. Skipping", key) - } else { - // TODO: Do we need to do any additional checking on the value? - dg[key] = val - } - } - dest[GlobalKey] = dg -} - -func copyMap(src map[string]interface{}) map[string]interface{} { - m := make(map[string]interface{}, len(src)) - for k, v := range src { - m[k] = v - } - return m -} - -// coalesceValues builds up a values map for a particular chart. -// -// Values in v will override the values in the chart. -func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, prefix string) { - subPrefix := concatPrefix(prefix, c.Metadata.Name) - for key, val := range c.Values { - if value, ok := v[key]; ok { - if value == nil { - // When the YAML value is null, we remove the value's key. - // This allows Helm's various sources of values (value files or --set) to - // remove incompatible keys from any previous chart, file, or set values. - delete(v, key) - } else if dest, ok := value.(map[string]interface{}); ok { - // if v[key] is a table, merge nv's val table into v[key]. - src, ok := val.(map[string]interface{}) - if !ok { - // If the original value is nil, there is nothing to coalesce, so we don't print - // the warning - if val != nil { - printf("warning: skipped value for %s.%s: Not a table.", subPrefix, key) - } - } else { - // Because v has higher precedence than nv, dest values override src - // values. - coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key)) - } - } - } else { - // If the key is not in v, copy it from nv. - v[key] = val - } - } -} - -// CoalesceTables merges a source map into a destination map. -// -// dest is considered authoritative. -func CoalesceTables(dst, src map[string]interface{}) map[string]interface{} { - return coalesceTablesFullKey(log.Printf, dst, src, "") -} - -// coalesceTablesFullKey merges a source map into a destination map. -// -// dest is considered authoritative. -func coalesceTablesFullKey(printf printFn, dst, src map[string]interface{}, prefix string) map[string]interface{} { - // When --reuse-values is set but there are no modifications yet, return new values - if src == nil { - return dst - } - if dst == nil { - return src - } - // Because dest has higher precedence than src, dest values override src - // values. - for key, val := range src { - fullkey := concatPrefix(prefix, key) - if dv, ok := dst[key]; ok && dv == nil { - delete(dst, key) - } else if !ok { - dst[key] = val - } else if istable(val) { - if istable(dv) { - coalesceTablesFullKey(printf, dv.(map[string]interface{}), val.(map[string]interface{}), fullkey) - } else { - printf("warning: cannot overwrite table with non table for %s (%v)", fullkey, val) - } - } else if istable(dv) && val != nil { - printf("warning: destination for %s is a table. Ignoring non-table value (%v)", fullkey, val) - } - } - return dst -} diff --git a/pkg/chartutil/compatible.go b/pkg/chartutil/compatible.go deleted file mode 100644 index 134df66c..00000000 --- a/pkg/chartutil/compatible.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - semver "github.com/Masterminds/semver/v3" -) - -// IsCompatibleRange compares a version to a constraint. -// It returns true if the version matches the constraint, and false in all other cases. -func IsCompatibleRange(constraint, ver string) bool { - sv, err := semver.NewVersion(ver) - if err != nil { - return false - } - - c, err := semver.NewConstraint(constraint) - if err != nil { - return false - } - return c.Check(sv) -} diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go deleted file mode 100644 index d93a5da8..00000000 --- a/pkg/chartutil/create.go +++ /dev/null @@ -1,686 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "io" - "os" - "path/filepath" - "regexp" - "strings" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chart/loader" -) - -// chartName is a regular expression for testing the supplied name of a chart. -// This regular expression is probably stricter than it needs to be. We can relax it -// somewhat. Newline characters, as well as $, quotes, +, parens, and % are known to be -// problematic. -var chartName = regexp.MustCompile("^[a-zA-Z0-9._-]+$") - -const ( - // ChartfileName is the default Chart file name. - ChartfileName = "Chart.yaml" - // ValuesfileName is the default values file name. - ValuesfileName = "values.yaml" - // SchemafileName is the default values schema file name. - SchemafileName = "values.schema.json" - // TemplatesDir is the relative directory name for templates. - TemplatesDir = "templates" - // ChartsDir is the relative directory name for charts dependencies. - ChartsDir = "charts" - // TemplatesTestsDir is the relative directory name for tests. - TemplatesTestsDir = TemplatesDir + sep + "tests" - // IgnorefileName is the name of the Helm ignore file. - IgnorefileName = ".helmignore" - // IngressFileName is the name of the example ingress file. - IngressFileName = TemplatesDir + sep + "ingress.yaml" - // DeploymentName is the name of the example deployment file. - DeploymentName = TemplatesDir + sep + "deployment.yaml" - // ServiceName is the name of the example service file. - ServiceName = TemplatesDir + sep + "service.yaml" - // ServiceAccountName is the name of the example serviceaccount file. - ServiceAccountName = TemplatesDir + sep + "serviceaccount.yaml" - // HorizontalPodAutoscalerName is the name of the example hpa file. - HorizontalPodAutoscalerName = TemplatesDir + sep + "hpa.yaml" - // NotesName is the name of the example NOTES.txt file. - NotesName = TemplatesDir + sep + "NOTES.txt" - // HelpersName is the name of the example helpers file. - HelpersName = TemplatesDir + sep + "_helpers.tpl" - // TestConnectionName is the name of the example test file. - TestConnectionName = TemplatesTestsDir + sep + "test-connection.yaml" -) - -// maxChartNameLength is lower than the limits we know of with certain file systems, -// and with certain Kubernetes fields. -const maxChartNameLength = 250 - -const sep = string(filepath.Separator) - -const defaultChartfile = `apiVersion: v2 -name: %s -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" -` - -const defaultValues = `# Default values for %s. -# 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 - # 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: {} - -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 - -autoscaling: - enabled: false - minReplicas: 1 - maxReplicas: 100 - targetCPUUtilizationPercentage: 80 - # targetMemoryUtilizationPercentage: 80 - -nodeSelector: {} - -tolerations: [] - -affinity: {} -` - -const defaultIgnore = `# 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/ -` - -const defaultIngress = `{{- if .Values.ingress.enabled -}} -{{- $fullName := include ".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 ".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 }} -` - -const defaultDeployment = `apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - {{- if not .Values.autoscaling.enabled }} - replicas: {{ .Values.replicaCount }} - {{- end }} - selector: - matchLabels: - {{- include ".selectorLabels" . | nindent 6 }} - template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include ".selectorLabels" . | nindent 8 }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include ".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: 80 - protocol: TCP - livenessProbe: - httpGet: - path: / - port: http - readinessProbe: - httpGet: - path: / - port: http - resources: - {{- toYaml .Values.resources | nindent 12 }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} -` - -const defaultService = `apiVersion: v1 -kind: Service -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - type: {{ .Values.service.type }} - ports: - - port: {{ .Values.service.port }} - targetPort: http - protocol: TCP - name: http - selector: - {{- include ".selectorLabels" . | nindent 4 }} -` - -const defaultServiceAccount = `{{- if .Values.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include ".serviceAccountName" . }} - labels: - {{- include ".labels" . | nindent 4 }} - {{- with .Values.serviceAccount.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -{{- end }} -` - -const defaultHorizontalPodAutoscaler = `{{- if .Values.autoscaling.enabled }} -apiVersion: autoscaling/v2beta1 -kind: HorizontalPodAutoscaler -metadata: - name: {{ include ".fullname" . }} - labels: - {{- include ".labels" . | nindent 4 }} -spec: - scaleTargetRef: - apiVersion: apps/v1 - kind: Deployment - name: {{ include ".fullname" . }} - minReplicas: {{ .Values.autoscaling.minReplicas }} - maxReplicas: {{ .Values.autoscaling.maxReplicas }} - metrics: - {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} - - type: Resource - resource: - name: cpu - targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} - {{- end }} - {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} - - type: Resource - resource: - name: memory - targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} - {{- end }} -{{- end }} -` - -const defaultNotes = `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 ".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 ".fullname" . }}' - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include ".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 ".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 }} -` - -const defaultHelpers = `{{/* -Expand the name of the chart. -*/}} -{{- define ".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 ".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 ".chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define ".labels" -}} -helm.sh/chart: {{ include ".chart" . }} -{{ include ".selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define ".selectorLabels" -}} -app.kubernetes.io/name: {{ include ".name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define ".serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include ".fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} -` - -const defaultTestConnection = `apiVersion: v1 -kind: Pod -metadata: - name: "{{ include ".fullname" . }}-test-connection" - labels: - {{- include ".labels" . | nindent 4 }} - annotations: - "helm.sh/hook": test -spec: - containers: - - name: wget - image: busybox - command: ['wget'] - args: ['{{ include ".fullname" . }}:{{ .Values.service.port }}'] - restartPolicy: Never -` - -// Stderr is an io.Writer to which error messages can be written -// -// In Helm 4, this will be replaced. It is needed in Helm 3 to preserve API backward -// compatibility. -var Stderr io.Writer = os.Stderr - -// CreateFrom creates a new chart, but scaffolds it from the src chart. -func CreateFrom(chartfile *chart.Metadata, dest, src string) error { - schart, err := loader.Load(src) - if err != nil { - return errors.Wrapf(err, "could not load %s", src) - } - - schart.Metadata = chartfile - - var updatedTemplates []*chart.File - - for _, template := range schart.Templates { - newData := transform(string(template.Data), schart.Name()) - updatedTemplates = append(updatedTemplates, &chart.File{Name: template.Name, Data: newData}) - } - - schart.Templates = updatedTemplates - b, err := yaml.Marshal(schart.Values) - if err != nil { - return errors.Wrap(err, "reading values file") - } - - var m map[string]interface{} - if err := yaml.Unmarshal(transform(string(b), schart.Name()), &m); err != nil { - return errors.Wrap(err, "transforming values file") - } - schart.Values = m - - // SaveDir looks for the file values.yaml when saving rather than the values - // key in order to preserve the comments in the YAML. The name placeholder - // needs to be replaced on that file. - for _, f := range schart.Raw { - if f.Name == ValuesfileName { - f.Data = transform(string(f.Data), schart.Name()) - } - } - - return SaveDir(schart, dest) -} - -// Create creates a new chart in a directory. -// -// Inside of dir, this will create a directory based on the name of -// chartfile.Name. It will then write the Chart.yaml into this directory and -// create the (empty) appropriate directories. -// -// The returned string will point to the newly created directory. It will be -// an absolute path, even if the provided base directory was relative. -// -// If dir does not exist, this will return an error. -// If Chart.yaml or any directories cannot be created, this will return an -// error. In such a case, this will attempt to clean up by removing the -// new chart directory. -func Create(name, dir string) (string, error) { - - // Sanity-check the name of a chart so user doesn't create one that causes problems. - if err := validateChartName(name); err != nil { - return "", err - } - - path, err := filepath.Abs(dir) - if err != nil { - return path, err - } - - if fi, err := os.Stat(path); err != nil { - return path, err - } else if !fi.IsDir() { - return path, errors.Errorf("no such directory %s", path) - } - - cdir := filepath.Join(path, name) - if fi, err := os.Stat(cdir); err == nil && !fi.IsDir() { - return cdir, errors.Errorf("file %s already exists and is not a directory", cdir) - } - - files := []struct { - path string - content []byte - }{ - { - // Chart.yaml - path: filepath.Join(cdir, ChartfileName), - content: []byte(fmt.Sprintf(defaultChartfile, name)), - }, - { - // values.yaml - path: filepath.Join(cdir, ValuesfileName), - content: []byte(fmt.Sprintf(defaultValues, name)), - }, - { - // .helmignore - path: filepath.Join(cdir, IgnorefileName), - content: []byte(defaultIgnore), - }, - { - // ingress.yaml - path: filepath.Join(cdir, IngressFileName), - content: transform(defaultIngress, name), - }, - { - // deployment.yaml - path: filepath.Join(cdir, DeploymentName), - content: transform(defaultDeployment, name), - }, - { - // service.yaml - path: filepath.Join(cdir, ServiceName), - content: transform(defaultService, name), - }, - { - // serviceaccount.yaml - path: filepath.Join(cdir, ServiceAccountName), - content: transform(defaultServiceAccount, name), - }, - { - // hpa.yaml - path: filepath.Join(cdir, HorizontalPodAutoscalerName), - content: transform(defaultHorizontalPodAutoscaler, name), - }, - { - // NOTES.txt - path: filepath.Join(cdir, NotesName), - content: transform(defaultNotes, name), - }, - { - // _helpers.tpl - path: filepath.Join(cdir, HelpersName), - content: transform(defaultHelpers, name), - }, - { - // test-connection.yaml - path: filepath.Join(cdir, TestConnectionName), - content: transform(defaultTestConnection, name), - }, - } - - for _, file := range files { - if _, err := os.Stat(file.path); err == nil { - // There is no handle to a preferred output stream here. - fmt.Fprintf(Stderr, "WARNING: File %q already exists. Overwriting.\n", file.path) - } - if err := writeFile(file.path, file.content); err != nil { - return cdir, err - } - } - // Need to add the ChartsDir explicitly as it does not contain any file OOTB - if err := os.MkdirAll(filepath.Join(cdir, ChartsDir), 0755); err != nil { - return cdir, err - } - return cdir, nil -} - -// transform performs a string replacement of the specified source for -// a given key with the replacement string -func transform(src, replacement string) []byte { - return []byte(strings.ReplaceAll(src, "", replacement)) -} - -func writeFile(name string, content []byte) error { - if err := os.MkdirAll(filepath.Dir(name), 0755); err != nil { - return err - } - return os.WriteFile(name, content, 0644) -} - -func validateChartName(name string) error { - if name == "" || len(name) > maxChartNameLength { - return fmt.Errorf("chart name must be between 1 and %d characters", maxChartNameLength) - } - if !chartName.MatchString(name) { - return fmt.Errorf("chart name must match the regular expression %q", chartName.String()) - } - return nil -} diff --git a/pkg/chartutil/dependencies.go b/pkg/chartutil/dependencies.go deleted file mode 100644 index 586d98de..00000000 --- a/pkg/chartutil/dependencies.go +++ /dev/null @@ -1,285 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "log" - "strings" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// ProcessDependencies checks through this chart's dependencies, processing accordingly. -func ProcessDependencies(c *chart.Chart, v Values) error { - if err := processDependencyEnabled(c, v, ""); err != nil { - return err - } - return processDependencyImportValues(c) -} - -// processDependencyConditions disables charts based on condition path value in values -func processDependencyConditions(reqs []*chart.Dependency, cvals Values, cpath string) { - if reqs == nil { - return - } - for _, r := range reqs { - for _, c := range strings.Split(strings.TrimSpace(r.Condition), ",") { - if len(c) > 0 { - // retrieve value - vv, err := cvals.PathValue(cpath + c) - if err == nil { - // if not bool, warn - if bv, ok := vv.(bool); ok { - r.Enabled = bv - break - } - - log.Printf("Warning: Condition path '%s' for chart %s returned non-bool value", c, r.Name) - } else if _, ok := err.(ErrNoValue); !ok { - // this is a real error - log.Printf("Warning: PathValue returned error %v", err) - } - } - } - } -} - -// processDependencyTags disables charts based on tags in values -func processDependencyTags(reqs []*chart.Dependency, cvals Values) { - if reqs == nil { - return - } - vt, err := cvals.Table("tags") - if err != nil { - return - } - for _, r := range reqs { - var hasTrue, hasFalse bool - for _, k := range r.Tags { - if b, ok := vt[k]; ok { - // if not bool, warn - if bv, ok := b.(bool); ok { - if bv { - hasTrue = true - } else { - hasFalse = true - } - } else { - log.Printf("Warning: Tag '%s' for chart %s returned non-bool value", k, r.Name) - } - } - } - if !hasTrue && hasFalse { - r.Enabled = false - } else if hasTrue || !hasTrue && !hasFalse { - r.Enabled = true - } - } -} - -func getAliasDependency(charts []*chart.Chart, dep *chart.Dependency) *chart.Chart { - for _, c := range charts { - if c == nil { - continue - } - if c.Name() != dep.Name { - continue - } - if !IsCompatibleRange(dep.Version, c.Metadata.Version) { - continue - } - - out := *c - md := *c.Metadata - out.Metadata = &md - - if dep.Alias != "" { - md.Name = dep.Alias - } - return &out - } - return nil -} - -// processDependencyEnabled removes disabled charts from dependencies -func processDependencyEnabled(c *chart.Chart, v map[string]interface{}, path string) error { - if c.Metadata.Dependencies == nil { - return nil - } - - var chartDependencies []*chart.Chart - // If any dependency is not a part of Chart.yaml - // then this should be added to chartDependencies. - // However, if the dependency is already specified in Chart.yaml - // we should not add it, as it would be anyways processed from Chart.yaml - -Loop: - for _, existing := range c.Dependencies() { - for _, req := range c.Metadata.Dependencies { - if existing.Name() == req.Name && IsCompatibleRange(req.Version, existing.Metadata.Version) { - continue Loop - } - } - chartDependencies = append(chartDependencies, existing) - } - - for _, req := range c.Metadata.Dependencies { - if chartDependency := getAliasDependency(c.Dependencies(), req); chartDependency != nil { - chartDependencies = append(chartDependencies, chartDependency) - } - if req.Alias != "" { - req.Name = req.Alias - } - } - c.SetDependencies(chartDependencies...) - - // set all to true - for _, lr := range c.Metadata.Dependencies { - lr.Enabled = true - } - cvals, err := CoalesceValues(c, v) - if err != nil { - return err - } - // flag dependencies as enabled/disabled - processDependencyTags(c.Metadata.Dependencies, cvals) - processDependencyConditions(c.Metadata.Dependencies, cvals, path) - // make a map of charts to remove - rm := map[string]struct{}{} - for _, r := range c.Metadata.Dependencies { - if !r.Enabled { - // remove disabled chart - rm[r.Name] = struct{}{} - } - } - // don't keep disabled charts in new slice - cd := []*chart.Chart{} - copy(cd, c.Dependencies()[:0]) - for _, n := range c.Dependencies() { - if _, ok := rm[n.Metadata.Name]; !ok { - cd = append(cd, n) - } - } - // don't keep disabled charts in metadata - cdMetadata := []*chart.Dependency{} - copy(cdMetadata, c.Metadata.Dependencies[:0]) - for _, n := range c.Metadata.Dependencies { - if _, ok := rm[n.Name]; !ok { - cdMetadata = append(cdMetadata, n) - } - } - - // recursively call self to process sub dependencies - for _, t := range cd { - subpath := path + t.Metadata.Name + "." - if err := processDependencyEnabled(t, cvals, subpath); err != nil { - return err - } - } - // set the correct dependencies in metadata - c.Metadata.Dependencies = nil - c.Metadata.Dependencies = append(c.Metadata.Dependencies, cdMetadata...) - c.SetDependencies(cd...) - - return nil -} - -// pathToMap creates a nested map given a YAML path in dot notation. -func pathToMap(path string, data map[string]interface{}) map[string]interface{} { - if path == "." { - return data - } - return set(parsePath(path), data) -} - -func set(path []string, data map[string]interface{}) map[string]interface{} { - if len(path) == 0 { - return nil - } - cur := data - for i := len(path) - 1; i >= 0; i-- { - cur = map[string]interface{}{path[i]: cur} - } - return cur -} - -// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. -func processImportValues(c *chart.Chart) error { - if c.Metadata.Dependencies == nil { - return nil - } - // combine chart values and empty config to get Values - cvals, err := CoalesceValues(c, nil) - if err != nil { - return err - } - b := make(map[string]interface{}) - // import values from each dependency if specified in import-values - for _, r := range c.Metadata.Dependencies { - var outiv []interface{} - for _, riv := range r.ImportValues { - switch iv := riv.(type) { - case map[string]interface{}: - child := iv["child"].(string) - parent := iv["parent"].(string) - - outiv = append(outiv, map[string]string{ - "child": child, - "parent": parent, - }) - - // get child table - vv, err := cvals.Table(r.Name + "." + child) - if err != nil { - log.Printf("Warning: ImportValues missing table from chart %s: %v", r.Name, err) - continue - } - // create value map from child to be merged into parent - b = CoalesceTables(cvals, pathToMap(parent, vv.AsMap())) - case string: - child := "exports." + iv - outiv = append(outiv, map[string]string{ - "child": child, - "parent": ".", - }) - vm, err := cvals.Table(r.Name + "." + child) - if err != nil { - log.Printf("Warning: ImportValues missing table: %v", err) - continue - } - b = CoalesceTables(b, vm.AsMap()) - } - } - // set our formatted import values - r.ImportValues = outiv - } - - // set the new values - c.Values = CoalesceTables(cvals, b) - - return nil -} - -// processDependencyImportValues imports specified chart values from child to parent. -func processDependencyImportValues(c *chart.Chart) error { - for _, d := range c.Dependencies() { - // recurse - if err := processDependencyImportValues(d); err != nil { - return err - } - } - return processImportValues(c) -} diff --git a/pkg/chartutil/errors.go b/pkg/chartutil/errors.go deleted file mode 100644 index e44b3a38..00000000 --- a/pkg/chartutil/errors.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" -) - -// ErrNoTable indicates that a chart does not have a matching table. -type ErrNoTable struct { - Key string -} - -func (e ErrNoTable) Error() string { return fmt.Sprintf("%q is not a table", e.Key) } - -type ErrEmpytArray struct { - Key string -} - -func (e ErrEmpytArray) Error() string { return fmt.Sprintf("%q is an empyt array", e.Key) } - -// ErrNoValue indicates that Values does not contain a key with a value -type ErrNoValue struct { - Key string -} - -func (e ErrNoValue) Error() string { return fmt.Sprintf("%q is not a value", e.Key) } diff --git a/pkg/chartutil/expand.go b/pkg/chartutil/expand.go deleted file mode 100644 index 2a327ccb..00000000 --- a/pkg/chartutil/expand.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "io" - "os" - "path/filepath" - - securejoin "github.com/cyphar/filepath-securejoin" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chart/loader" -) - -// Expand uncompresses and extracts a chart into the specified directory. -func Expand(dir string, r io.Reader) error { - files, err := loader.LoadArchiveFiles(r) - if err != nil { - return err - } - - // Get the name of the chart - var chartName string - for _, file := range files { - if file.Name == "Chart.yaml" { - ch := &chart.Metadata{} - if err := yaml.Unmarshal(file.Data, ch); err != nil { - return errors.Wrap(err, "cannot load Chart.yaml") - } - chartName = ch.Name - } - } - if chartName == "" { - return errors.New("chart name not specified") - } - - // Find the base directory - chartdir, err := securejoin.SecureJoin(dir, chartName) - if err != nil { - return err - } - - // Copy all files verbatim. We don't parse these files because parsing can remove - // comments. - for _, file := range files { - outpath, err := securejoin.SecureJoin(chartdir, file.Name) - if err != nil { - return err - } - - // Make sure the necessary subdirs get created. - basedir := filepath.Dir(outpath) - if err := os.MkdirAll(basedir, 0755); err != nil { - return err - } - - if err := os.WriteFile(outpath, file.Data, 0644); err != nil { - return err - } - } - - return nil -} - -// ExpandFile expands the src file into the dest directory. -func ExpandFile(dest, src string) error { - h, err := os.Open(src) - if err != nil { - return err - } - defer h.Close() - return Expand(dest, h) -} diff --git a/pkg/chartutil/jsonschema.go b/pkg/chartutil/jsonschema.go deleted file mode 100644 index 1a0c3976..00000000 --- a/pkg/chartutil/jsonschema.go +++ /dev/null @@ -1,87 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "bytes" - "fmt" - "strings" - - "github.com/pkg/errors" - "github.com/xeipuuv/gojsonschema" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// ValidateAgainstSchema checks that values does not violate the structure laid out in schema -func ValidateAgainstSchema(chrt *chart.Chart, values map[string]interface{}) error { - var sb strings.Builder - if chrt.Schema != nil { - err := ValidateAgainstSingleSchema(values, chrt.Schema) - if err != nil { - sb.WriteString(fmt.Sprintf("%s:\n", chrt.Name())) - sb.WriteString(err.Error()) - } - } - - // For each dependency, recursively call this function with the coalesced values - for _, subchart := range chrt.Dependencies() { - subchartValues := values[subchart.Name()].(map[string]interface{}) - if err := ValidateAgainstSchema(subchart, subchartValues); err != nil { - sb.WriteString(err.Error()) - } - } - - if sb.Len() > 0 { - return errors.New(sb.String()) - } - - return nil -} - -// ValidateAgainstSingleSchema checks that values does not violate the structure laid out in this schema -func ValidateAgainstSingleSchema(values Values, schemaJSON []byte) error { - valuesData, err := yaml.Marshal(values) - if err != nil { - return err - } - valuesJSON, err := yaml.YAMLToJSON(valuesData) - if err != nil { - return err - } - if bytes.Equal(valuesJSON, []byte("null")) { - valuesJSON = []byte("{}") - } - schemaLoader := gojsonschema.NewBytesLoader(schemaJSON) - valuesLoader := gojsonschema.NewBytesLoader(valuesJSON) - - result, err := gojsonschema.Validate(schemaLoader, valuesLoader) - if err != nil { - return err - } - - if !result.Valid() { - var sb strings.Builder - for _, desc := range result.Errors() { - sb.WriteString(fmt.Sprintf("- %s\n", desc)) - } - return errors.New(sb.String()) - } - - return nil -} diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go deleted file mode 100644 index e2c7daca..00000000 --- a/pkg/chartutil/save.go +++ /dev/null @@ -1,244 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "archive/tar" - "compress/gzip" - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -var headerBytes = []byte("+aHR0cHM6Ly95b3V0dS5iZS96OVV6MWljandyTQo=") - -// SaveDir saves a chart as files in a directory. -// -// This takes the chart name, and creates a new subdirectory inside of the given dest -// directory, writing the chart's contents to that subdirectory. -func SaveDir(c *chart.Chart, dest string) error { - // Create the chart directory - outdir := filepath.Join(dest, c.Name()) - if fi, err := os.Stat(outdir); err == nil && !fi.IsDir() { - return errors.Errorf("file %s already exists and is not a directory", outdir) - } - if err := os.MkdirAll(outdir, 0755); err != nil { - return err - } - - // Save the chart file. - if err := SaveChartfile(filepath.Join(outdir, ChartfileName), c.Metadata); err != nil { - return err - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == ValuesfileName { - vf := filepath.Join(outdir, ValuesfileName) - if err := writeFile(vf, f.Data); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - filename := filepath.Join(outdir, SchemafileName) - if err := writeFile(filename, c.Schema); err != nil { - return err - } - } - - // Save templates and files - for _, o := range [][]*chart.File{c.Templates, c.Files} { - for _, f := range o { - n := filepath.Join(outdir, f.Name) - if err := writeFile(n, f.Data); err != nil { - return err - } - } - } - - // Save dependencies - base := filepath.Join(outdir, ChartsDir) - for _, dep := range c.Dependencies() { - // Here, we write each dependency as a tar file. - if _, err := Save(dep, base); err != nil { - return errors.Wrapf(err, "saving %s", dep.ChartFullPath()) - } - } - return nil -} - -// Save creates an archived chart to the given directory. -// -// This takes an existing chart and a destination directory. -// -// If the directory is /foo, and the chart is named bar, with version 1.0.0, this -// will generate /foo/bar-1.0.0.tgz. -// -// This returns the absolute path to the chart archive file. -func Save(c *chart.Chart, outDir string) (string, error) { - if err := c.Validate(); err != nil { - return "", errors.Wrap(err, "chart validation") - } - - filename := fmt.Sprintf("%s-%s.tgz", c.Name(), c.Metadata.Version) - filename = filepath.Join(outDir, filename) - dir := filepath.Dir(filename) - if stat, err := os.Stat(dir); err != nil { - if os.IsNotExist(err) { - if err2 := os.MkdirAll(dir, 0755); err2 != nil { - return "", err2 - } - } else { - return "", errors.Wrapf(err, "stat %s", dir) - } - } else if !stat.IsDir() { - return "", errors.Errorf("is not a directory: %s", dir) - } - - f, err := os.Create(filename) - if err != nil { - return "", err - } - - // Wrap in gzip writer - zipper := gzip.NewWriter(f) - zipper.Header.Extra = headerBytes - zipper.Header.Comment = "Helm" - - // Wrap in tar writer - twriter := tar.NewWriter(zipper) - rollback := false - defer func() { - twriter.Close() - zipper.Close() - f.Close() - if rollback { - os.Remove(filename) - } - }() - - if err := writeTarContents(twriter, c, ""); err != nil { - rollback = true - return filename, err - } - return filename, nil -} - -func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { - base := filepath.Join(prefix, c.Name()) - - // Pull out the dependencies of a v1 Chart, since there's no way - // to tell the serializer to skip a field for just this use case - savedDependencies := c.Metadata.Dependencies - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Metadata.Dependencies = nil - } - // Save Chart.yaml - cdata, err := yaml.Marshal(c.Metadata) - if c.Metadata.APIVersion == chart.APIVersionV1 { - c.Metadata.Dependencies = savedDependencies - } - if err != nil { - return err - } - if err := writeToTar(out, filepath.Join(base, ChartfileName), cdata); err != nil { - return err - } - - // Save Chart.lock - // TODO: remove the APIVersion check when APIVersionV1 is not used anymore - if c.Metadata.APIVersion == chart.APIVersionV2 { - if c.Lock != nil { - ldata, err := yaml.Marshal(c.Lock) - if err != nil { - return err - } - if err := writeToTar(out, filepath.Join(base, "Chart.lock"), ldata); err != nil { - return err - } - } - } - - // Save values.yaml - for _, f := range c.Raw { - if f.Name == ValuesfileName { - if err := writeToTar(out, filepath.Join(base, ValuesfileName), f.Data); err != nil { - return err - } - } - } - - // Save values.schema.json if it exists - if c.Schema != nil { - if !json.Valid(c.Schema) { - return errors.New("Invalid JSON in " + SchemafileName) - } - if err := writeToTar(out, filepath.Join(base, SchemafileName), c.Schema); err != nil { - return err - } - } - - // Save templates - for _, f := range c.Templates { - n := filepath.Join(base, f.Name) - if err := writeToTar(out, n, f.Data); err != nil { - return err - } - } - - // Save files - for _, f := range c.Files { - n := filepath.Join(base, f.Name) - if err := writeToTar(out, n, f.Data); err != nil { - return err - } - } - - // Save dependencies - for _, dep := range c.Dependencies() { - if err := writeTarContents(out, dep, filepath.Join(base, ChartsDir)); err != nil { - return err - } - } - return nil -} - -// writeToTar writes a single file to a tar archive. -func writeToTar(out *tar.Writer, name string, body []byte) error { - // TODO: Do we need to create dummy parent directory names if none exist? - h := &tar.Header{ - Name: filepath.ToSlash(name), - Mode: 0644, - Size: int64(len(body)), - ModTime: time.Now(), - } - if err := out.WriteHeader(h); err != nil { - return err - } - _, err := out.Write(body) - return err -} diff --git a/pkg/chartutil/validate_name.go b/pkg/chartutil/validate_name.go deleted file mode 100644 index 05c090cb..00000000 --- a/pkg/chartutil/validate_name.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "regexp" - - "github.com/pkg/errors" -) - -// validName is a regular expression for resource names. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* -// -// This follows the above regular expression (but requires a full string match, not partial). -// -// The Kubernetes documentation is here, though it is not entirely correct: -// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -var validName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`) - -var ( - // errMissingName indicates that a release (name) was not provided. - errMissingName = errors.New("no name provided") - - // errInvalidName indicates that an invalid release name was provided - errInvalidName = fmt.Errorf( - "invalid release name, must match regex %s and the length must not be longer than 53", - validName.String()) - - // errInvalidKubernetesName indicates that the name does not meet the Kubernetes - // restrictions on metadata names. - errInvalidKubernetesName = fmt.Errorf( - "invalid metadata name, must match regex %s and the length must not be longer than 253", - validName.String()) -) - -const ( - // According to the Kubernetes docs (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#rfc-1035-label-names) - // some resource names have a max length of 63 characters while others have a max - // length of 253 characters. As we cannot be sure the resources used in a chart, we - // therefore need to limit it to 63 chars and reserve 10 chars for additional part to name - // of the resource. The reason is that chart maintainers can use release name as part of - // the resource name (and some additional chars). - maxReleaseNameLen = 53 - // maxMetadataNameLen is the maximum length Kubernetes allows for any name. - maxMetadataNameLen = 253 -) - -// ValidateReleaseName performs checks for an entry for a Helm release name -// -// For Helm to allow a name, it must be below a certain character count (53) and also match -// a regular expression. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* -// -// This follows the above regular expression (but requires a full string match, not partial). -// -// The Kubernetes documentation is here, though it is not entirely correct: -// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -func ValidateReleaseName(name string) error { - // This case is preserved for backwards compatibility - if name == "" { - return errMissingName - - } - if len(name) > maxReleaseNameLen || !validName.MatchString(name) { - return errInvalidName - } - return nil -} - -// ValidateMetadataName validates the name field of a Kubernetes metadata object. -// -// Empty strings, strings longer than 253 chars, or strings that don't match the regexp -// will fail. -// -// According to the Kubernetes help text, the regular expression it uses is: -// -// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* -// -// This follows the above regular expression (but requires a full string match, not partial). -// -// The Kubernetes documentation is here, though it is not entirely correct: -// https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names -// -// Deprecated: remove in Helm 4. Name validation now uses rules defined in -// pkg/lint/rules.validateMetadataNameFunc() -func ValidateMetadataName(name string) error { - if name == "" || len(name) > maxMetadataNameLen || !validName.MatchString(name) { - return errInvalidKubernetesName - } - return nil -} diff --git a/pkg/chartutil/values.go b/pkg/chartutil/values.go deleted file mode 100644 index 192339b5..00000000 --- a/pkg/chartutil/values.go +++ /dev/null @@ -1,255 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package chartutil - -import ( - "fmt" - "io" - "os" - "strings" - - "github.com/pkg/errors" - yamlv3 "gopkg.in/yaml.v3" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// GlobalKey is the name of the Values key that is used for storing global vars. -const GlobalKey = "global" - -// Values represents a collection of chart values. -type Values map[string]interface{} - -// YAML encodes the Values into a YAML string. -func (v Values) YAML() (string, error) { - b, err := yaml.Marshal(v) - return string(b), err -} - -// Table gets a table (YAML subsection) from a Values object. -// -// The table is returned as a Values. -// -// Compound table names may be specified with dots: -// -// foo.bar -// -// The above will be evaluated as "The table bar inside the table -// foo". -// -// Also supports YAML arrays (only with index 0): -// foo[0].bar -// -// The above will be evaluated as "The table bar as the first -// element of the array foo". -// -// An ErrNoTable is returned if the table does not exist. -func (v Values) Table(name string) (Values, error) { - table := v - var err error - - for _, n := range parsePath(name) { - if table, err = tableLookup(table, n); err != nil { - break - } - } - return table, err -} - -// AsMap is a utility function for converting Values to a map[string]interface{}. -// -// It protects against nil map panics. -func (v Values) AsMap() map[string]interface{} { - if len(v) == 0 { - return map[string]interface{}{} - } - return v -} - -// Encode writes serialized Values information to the given io.Writer. -func (v Values) Encode(w io.Writer) error { - out, err := yaml.Marshal(v) - if err != nil { - return err - } - _, err = w.Write(out) - return err -} - -func tableLookup(v Values, simple string) (Values, error) { - if strings.HasSuffix(simple, "[0]") { - return arryLookup(v, simple) - } - - v2, ok := v[simple] - if !ok { - return v, ErrNoTable{simple} - } - if vv, ok := v2.(map[string]interface{}); ok { - return vv, nil - } - - // This catches a case where a value is of type Values, but doesn't (for some - // reason) match the map[string]interface{}. This has been observed in the - // wild, and might be a result of a nil map of type Values. - if vv, ok := v2.(Values); ok { - return vv, nil - } - - return Values{}, ErrNoTable{simple} -} - -func arryLookup(v Values, simple string) (Values, error) { - v2, ok := v[simple[:(len(simple)-3)]] - if !ok { - return v, ErrNoTable{fmt.Sprintf("Yaml key %s does not exist", simple)} - } - if v3, ok := v2.([]interface{}); ok { - if len(v3) == 0 { - return Values{}, ErrEmpytArray{simple} - } - if vv, ok := v3[0].(map[string]interface{}); ok { - return vv, nil - } - if vv, ok := v3[0].(Values); ok { - return vv, nil - } - return Values{}, ErrNoTable{simple} - } - - return Values{}, ErrNoTable{simple} -} - -// ReadValues will parse YAML byte data into a Values. -func ReadValues(data []byte) (vals Values, err error) { - err = yaml.Unmarshal(data, &vals) - if len(vals) == 0 { - vals = Values{} - } - return vals, err -} - -// ReadValuesFile will parse a YAML file into a map of values. -func ReadValuesFile(filename string) (Values, error) { - data, err := os.ReadFile(filename) - if err != nil { - return map[string]interface{}{}, err - } - return ReadValues(data) -} - -// ReadYamlFileToNode will parse a YAML file into a yaml Node. -func ReadYamlFileToNode(filename string) (node yamlv3.Node, err error) { - data, err := os.ReadFile(filename) - if err != nil { - return yamlv3.Node{}, err - } - - err = yamlv3.Unmarshal(data, &node) - return node, err -} - -// ReleaseOptions represents the additional release options needed -// for the composition of the final values struct -type ReleaseOptions struct { - Name string - Namespace string - Revision int - IsUpgrade bool - IsInstall bool -} - -// ToRenderValues composes the struct from the data coming from the Releases, Charts and Values files -// -// This takes both ReleaseOptions and Capabilities to merge into the render values. -func ToRenderValues(chrt *chart.Chart, chrtVals map[string]interface{}, options ReleaseOptions, caps *Capabilities) (Values, error) { - if caps == nil { - caps = DefaultCapabilities - } - top := map[string]interface{}{ - "Chart": chrt.Metadata, - "Capabilities": caps, - "Release": map[string]interface{}{ - "Name": options.Name, - "Namespace": options.Namespace, - "IsUpgrade": options.IsUpgrade, - "IsInstall": options.IsInstall, - "Revision": options.Revision, - "Service": "Helm", - }, - } - - vals, err := CoalesceValues(chrt, chrtVals) - if err != nil { - return top, err - } - - if err := ValidateAgainstSchema(chrt, vals); err != nil { - errFmt := "values don't meet the specifications of the schema(s) in the following chart(s):\n%s" - return top, fmt.Errorf(errFmt, err.Error()) - } - - top["Values"] = vals - return top, nil -} - -// istable is a special-purpose function to see if the present thing matches the definition of a YAML table. -func istable(v interface{}) bool { - _, ok := v.(map[string]interface{}) - return ok -} - -// PathValue takes a path that traverses a YAML structure and returns the value at the end of that path. -// The path starts at the root of the YAML structure and is comprised of YAML keys separated by periods. -// Given the following YAML data the value at path "chapter.one.title" is "Loomings". -// -// chapter: -// one: -// title: "Loomings" -func (v Values) PathValue(path string) (interface{}, error) { - if path == "" { - return nil, errors.New("YAML path cannot be empty") - } - return v.pathValue(parsePath(path)) -} - -func (v Values) pathValue(path []string) (interface{}, error) { - if len(path) == 1 { - // if exists must be root key not table - if _, ok := v[path[0]]; ok && !istable(v[path[0]]) { - return v[path[0]], nil - } - return nil, ErrNoValue{path[0]} - } - - key, path := path[len(path)-1], path[:len(path)-1] - // get our table for table path - t, err := v.Table(joinPath(path...)) - if err != nil { - return nil, ErrNoValue{key} - } - // check table for key and ensure value is not a table - if k, ok := t[key]; ok && !istable(k) { - return k, nil - } - return nil, ErrNoValue{key} -} - -func parsePath(key string) []string { return strings.Split(key, ".") } - -func joinPath(path ...string) string { return strings.Join(path, ".") } diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go deleted file mode 100644 index 95855671..00000000 --- a/pkg/engine/engine.go +++ /dev/null @@ -1,416 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "fmt" - "log" - "path" - "path/filepath" - "sort" - "strings" - "text/template" - - "github.com/pkg/errors" - "k8s.io/client-go/rest" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" -) - -// Engine is an implementation of the Helm rendering implementation for templates. -type Engine struct { - // If strict is enabled, template rendering will fail if a template references - // a value that was not passed in. - Strict bool - // In LintMode, some 'required' template values may be missing, so don't fail - LintMode bool - // the rest config to connect to the kubernetes api - config *rest.Config -} - -// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. -// -// Render can be called repeatedly on the same engine. -// -// This will look in the chart's 'templates' data (e.g. the 'templates/' directory) -// and attempt to render the templates there using the values passed in. -// -// Values are scoped to their templates. A dependency template will not have -// access to the values set for its parent. If chart "foo" includes chart "bar", -// "bar" will not have access to the values for "foo". -// -// Values should be prepared with something like `chartutils.ReadValues`. -// -// Values are passed through the templates according to scope. If the top layer -// chart includes the chart foo, which includes the chart bar, the values map -// will be examined for a table called "foo". If "foo" is found in vals, -// that section of the values will be passed into the "foo" chart. And if that -// section contains a value named "bar", that value will be passed on to the -// bar chart during render time. -func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { - tmap := allTemplates(chrt, values) - return e.render(tmap) -} - -// Render takes a chart, optional values, and value overrides, and attempts to -// render the Go templates using the default options. -func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { - return new(Engine).Render(chrt, values) -} - -// RenderWithClient takes a chart, optional values, and value overrides, and attempts to -// render the Go templates using the default options. This engine is client aware and so can have template -// functions that interact with the client -func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) { - return Engine{ - config: config, - }.Render(chrt, values) -} - -// renderable is an object that can be rendered. -type renderable struct { - // tpl is the current template. - tpl string - // vals are the values to be supplied to the template. - vals chartutil.Values - // namespace prefix to the templates of the current chart - basePath string -} - -const warnStartDelim = "HELM_ERR_START" -const warnEndDelim = "HELM_ERR_END" -const recursionMaxNums = 1000 - -func warnWrap(warn string) string { - return warnStartDelim + warn + warnEndDelim -} - -// initFunMap creates the Engine's FuncMap and adds context-specific functions. -func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { - funcMap := funcMap() - includedNames := make(map[string]int) - - // Add the 'include' function here so we can close over t. - funcMap["include"] = func(name string, data interface{}) (string, error) { - var buf strings.Builder - if v, ok := includedNames[name]; ok { - if v > recursionMaxNums { - return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name) - } - includedNames[name]++ - } else { - includedNames[name] = 1 - } - err := t.ExecuteTemplate(&buf, name, data) - includedNames[name]-- - return buf.String(), err - } - - // Add the 'tpl' function here - funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) { - basePath, err := vals.PathValue("Template.BasePath") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl) - } - - templateName, err := vals.PathValue("Template.Name") - if err != nil { - return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl) - } - - templates := map[string]renderable{ - templateName.(string): { - tpl: tpl, - vals: vals, - basePath: basePath.(string), - }, - } - - result, err := e.renderWithReferences(templates, referenceTpls) - if err != nil { - return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl) - } - return result[templateName.(string)], nil - } - - // Add the `required` function here so we can use lintMode - funcMap["required"] = func(warn string, val interface{}) (interface{}, error) { - if val == nil { - if e.LintMode { - // Don't fail on missing required values when linting - log.Printf("[INFO] Missing required value: %s", warn) - return "", nil - } - return val, errors.Errorf(warnWrap(warn)) - } else if _, ok := val.(string); ok { - if val == "" { - if e.LintMode { - // Don't fail on missing required values when linting - log.Printf("[INFO] Missing required value: %s", warn) - return "", nil - } - return val, errors.Errorf(warnWrap(warn)) - } - } - return val, nil - } - - // Override sprig fail function for linting and wrapping message - funcMap["fail"] = func(msg string) (string, error) { - if e.LintMode { - // Don't fail when linting - log.Printf("[INFO] Fail: %s", msg) - return "", nil - } - return "", errors.New(warnWrap(msg)) - } - - // If we are not linting and have a cluster connection, provide a Kubernetes-backed - // implementation. - if !e.LintMode && e.config != nil { - funcMap["lookup"] = NewLookupFunction(e.config) - } - - t.Funcs(funcMap) -} - -// render takes a map of templates/values and renders them. -func (e Engine) render(tpls map[string]renderable) (map[string]string, error) { - return e.renderWithReferences(tpls, tpls) -} - -// renderWithReferences takes a map of templates/values to render, and a map of -// templates which can be referenced within them. -func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) { - - // Basically, what we do here is start with an empty parent template and then - // build up a list of templates -- one for each file. Once all of the templates - // have been parsed, we loop through again and execute every template. - // - // The idea with this process is to make it possible for more complex templates - // to share common blocks, but to make the entire thing feel like a file-based - // template engine. - defer func() { - if r := recover(); r != nil { - err = errors.Errorf("rendering template failed: %v", r) - } - }() - t := template.New("gotpl") - if e.Strict { - t.Option("missingkey=error") - } else { - // Not that zero will attempt to add default values for types it knows, - // but will still emit for others. We mitigate that later. - t.Option("missingkey=zero") - } - - e.initFunMap(t, referenceTpls) - - // We want to parse the templates in a predictable order. The order favors - // higher-level (in file system) templates over deeply nested templates. - keys := sortTemplates(tpls) - referenceKeys := sortTemplates(referenceTpls) - - errs := make([]error, 0) - for _, filename := range keys { - r := tpls[filename] - if _, err := t.New(filename).Parse(r.tpl); err != nil { - errs = append(errs, cleanupParseError(filename, err)) - } - } - if len(errs) > 0 { - return map[string]string{}, &LintError{errors: errs} - } - - // Adding the reference templates to the template context - // so they can be referenced in the tpl function - errs = make([]error, 0) - for _, filename := range referenceKeys { - if t.Lookup(filename) == nil { - r := referenceTpls[filename] - if _, err := t.New(filename).Parse(r.tpl); err != nil { - errs = append(errs, cleanupParseError(filename, err)) - } - } - } - if len(errs) > 0 { - return map[string]string{}, &LintError{errors: errs} - } - - rendered = make(map[string]string, len(keys)) - errs = make([]error, 0) - for _, filename := range keys { - // Don't render partials. We don't care out the direct output of partials. - // They are only included from other templates. - if strings.HasPrefix(path.Base(filename), "_") { - continue - } - // At render time, add information about the template that is being rendered. - vals := tpls[filename].vals - vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath} - var buf strings.Builder - if err := t.ExecuteTemplate(&buf, filename, vals); err != nil { - errs = append(errs, cleanupParseError(filename, err)) - } - - // Work around the issue where Go will emit "" even if Options(missing=zero) - // is set. Since missing=error will never get here, we do not need to handle - // the Strict case. - rendered[filename] = strings.ReplaceAll(buf.String(), "", "") - } - if len(errs) > 0 { - return map[string]string{}, &LintError{errors: errs} - } - - return rendered, nil -} - -func cleanupParseError(filename string, err error) error { - - tokens := strings.Split(err.Error(), ": ") - - if len(tokens) == 1 { - // This might happen if a non-templating error occurs - return fmt.Errorf("parse error in (%s): %s", filename, err) - } - - // The first token is "template" - // The second token is either "filename:lineno" or "filename:lineNo:columnNo" - location := tokens[1] - //// The remaining tokens make up a stacktrace-like chain, ending with the relevant error - errMsg := tokens[len(tokens)-1] - return fmt.Errorf("parse error at (%s): %s", string(location), errMsg) -} - -//func cleanupExecError(filename string, err error) error { -//if _, isExecError := err.(template.ExecError); !isExecError { -//return err -//} - -//tokens := strings.SplitN(err.Error(), ": ", 3) -//if len(tokens) != 3 { -//// This might happen if a non-templating error occurs -//return fmt.Errorf("execution error in (%s): %s", filename, err) -//} - -//// The first token is "template" -//// The second token is either "filename:lineno" or "filename:lineNo:columnNo" -//location := tokens[1] - -//warnRegex := regexp.MustCompile(warnStartDelim + `((?s).*)` + warnEndDelim) - -//parts := warnRegex.FindStringSubmatch(tokens[2]) -//if len(parts) >= 2 { -//return fmt.Errorf("execution error at (%s): %s", string(location), parts[1]) -//} - -//return err -//} - -func sortTemplates(tpls map[string]renderable) []string { - keys := make([]string, len(tpls)) - i := 0 - for key := range tpls { - keys[i] = key - i++ - } - sort.Sort(sort.Reverse(byPathLen(keys))) - return keys -} - -type byPathLen []string - -func (p byPathLen) Len() int { return len(p) } -func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] } -func (p byPathLen) Less(i, j int) bool { - a, b := p[i], p[j] - ca, cb := strings.Count(a, "/"), strings.Count(b, "/") - if ca == cb { - return strings.Compare(a, b) == -1 - } - return ca < cb -} - -// allTemplates returns all templates for a chart and its dependencies. -// -// As it goes, it also prepares the values in a scope-sensitive manner. -func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable { - templates := make(map[string]renderable) - recAllTpls(c, templates, vals) - return templates -} - -// recAllTpls recurses through the templates in a chart. -// -// As it recurses, it also sets the values to be appropriate for the template -// scope. -func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) map[string]interface{} { - subCharts := make(map[string]interface{}) - chartMetaData := struct { - chart.Metadata - IsRoot bool - }{*c.Metadata, c.IsRoot()} - - next := map[string]interface{}{ - "Chart": chartMetaData, - "Files": newFiles(c.Files), - "Release": vals["Release"], - "Capabilities": vals["Capabilities"], - "Values": make(chartutil.Values), - "Subcharts": subCharts, - } - - // If there is a {{.Values.ThisChart}} in the parent metadata, - // copy that into the {{.Values}} for this template. - if c.IsRoot() { - next["Values"] = vals["Values"] - } else if vs, err := vals.Table("Values." + c.Name()); err == nil { - next["Values"] = vs - } - - for _, child := range c.Dependencies() { - subCharts[child.Name()] = recAllTpls(child, templates, next) - } - - newParentID := c.ChartFullPath() - for _, t := range c.Templates { - if !isTemplateValid(c, t.Name) { - continue - } - templates[path.Join(newParentID, t.Name)] = renderable{ - tpl: string(t.Data), - vals: next, - basePath: path.Join(newParentID, "templates"), - } - } - - return next -} - -// isTemplateValid returns true if the template is valid for the chart type -func isTemplateValid(ch *chart.Chart, templateName string) bool { - if isLibraryChart(ch) { - return strings.HasPrefix(filepath.Base(templateName), "_") - } - return true -} - -// isLibraryChart returns true if the chart is a library chart -func isLibraryChart(c *chart.Chart) bool { - return strings.EqualFold(c.Metadata.Type, "library") -} diff --git a/pkg/engine/error.go b/pkg/engine/error.go deleted file mode 100644 index eb87a38c..00000000 --- a/pkg/engine/error.go +++ /dev/null @@ -1,25 +0,0 @@ -package engine - -type LintError struct { - message string - errors []error -} - -func NewLintError(msg string, errs ...error) *LintError { - return &LintError{ - errors: errs, - message: msg, - } -} - -func (err *LintError) Errors() []error { - return err.errors -} - -func (err *LintError) Error() string { - message := "" - for _, e := range err.errors { - message += e.Error() + "\n" - } - return message -} diff --git a/pkg/engine/files.go b/pkg/engine/files.go deleted file mode 100644 index 48199de5..00000000 --- a/pkg/engine/files.go +++ /dev/null @@ -1,160 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "encoding/base64" - "path" - "strings" - - "github.com/gobwas/glob" - - "github.com/mrjosh/helm-ls/pkg/chart" -) - -// files is a map of files in a chart that can be accessed from a template. -type files map[string][]byte - -// NewFiles creates a new files from chart files. -// Given an []*chart.File (the format for files in a chart.Chart), extract a map of files. -func newFiles(from []*chart.File) files { - files := make(map[string][]byte) - for _, f := range from { - files[f.Name] = f.Data - } - return files -} - -// GetBytes gets a file by path. -// -// The returned data is raw. In a template context, this is identical to calling -// {{index .Files $path}}. -// -// This is intended to be accessed from within a template, so a missed key returns -// an empty []byte. -func (f files) GetBytes(name string) []byte { - if v, ok := f[name]; ok { - return v - } - return []byte{} -} - -// Get returns a string representation of the given file. -// -// Fetch the contents of a file as a string. It is designed to be called in a -// template. -// -// {{.Files.Get "foo"}} -func (f files) Get(name string) string { - return string(f.GetBytes(name)) -} - -// Glob takes a glob pattern and returns another files object only containing -// matched files. -// -// This is designed to be called from a template. -// -// {{ range $name, $content := .Files.Glob("foo/**") }} -// {{ $name }}: | -// {{ .Files.Get($name) | indent 4 }}{{ end }} -func (f files) Glob(pattern string) files { - g, err := glob.Compile(pattern, '/') - if err != nil { - g, _ = glob.Compile("**") - } - - nf := newFiles(nil) - for name, contents := range f { - if g.Match(name) { - nf[name] = contents - } - } - - return nf -} - -// AsConfig turns a Files group and flattens it to a YAML map suitable for -// including in the 'data' section of a Kubernetes ConfigMap definition. -// Duplicate keys will be overwritten, so be aware that your file names -// (regardless of path) should be unique. -// -// This is designed to be called from a template, and will return empty string -// (via toYAML function) if it cannot be serialized to YAML, or if the Files -// object is nil. -// -// The output will not be indented, so you will want to pipe this to the -// 'indent' template function. -// -// data: -// {{ .Files.Glob("config/**").AsConfig() | indent 4 }} -func (f files) AsConfig() string { - if f == nil { - return "" - } - - m := make(map[string]string) - - // Explicitly convert to strings, and file names - for k, v := range f { - m[path.Base(k)] = string(v) - } - - return toYAML(m) -} - -// AsSecrets returns the base64-encoded value of a Files object suitable for -// including in the 'data' section of a Kubernetes Secret definition. -// Duplicate keys will be overwritten, so be aware that your file names -// (regardless of path) should be unique. -// -// This is designed to be called from a template, and will return empty string -// (via toYAML function) if it cannot be serialized to YAML, or if the Files -// object is nil. -// -// The output will not be indented, so you will want to pipe this to the -// 'indent' template function. -// -// data: -// {{ .Files.Glob("secrets/*").AsSecrets() }} -func (f files) AsSecrets() string { - if f == nil { - return "" - } - - m := make(map[string]string) - - for k, v := range f { - m[path.Base(k)] = base64.StdEncoding.EncodeToString(v) - } - - return toYAML(m) -} - -// Lines returns each line of a named file (split by "\n") as a slice, so it can -// be ranged over in your templates. -// -// This is designed to be called from a template. -// -// {{ range .Files.Lines "foo/bar.html" }} -// {{ . }}{{ end }} -func (f files) Lines(path string) []string { - if f == nil || f[path] == nil { - return []string{} - } - - return strings.Split(string(f[path]), "\n") -} diff --git a/pkg/engine/files_test.go b/pkg/engine/files_test.go deleted file mode 100644 index 4b37724f..00000000 --- a/pkg/engine/files_test.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Helm Authors. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -var cases = []struct { - path, data string -}{ - {"ship/captain.txt", "The Captain"}, - {"ship/stowaway.txt", "Legatt"}, - {"story/name.txt", "The Secret Sharer"}, - {"story/author.txt", "Joseph Conrad"}, - {"multiline/test.txt", "bar\nfoo"}, -} - -func getTestFiles() files { - a := make(files, len(cases)) - for _, c := range cases { - a[c.path] = []byte(c.data) - } - return a -} - -func TestNewFiles(t *testing.T) { - files := getTestFiles() - if len(files) != len(cases) { - t.Errorf("Expected len() = %d, got %d", len(cases), len(files)) - } - - for i, f := range cases { - if got := string(files.GetBytes(f.path)); got != f.data { - t.Errorf("%d: expected %q, got %q", i, f.data, got) - } - if got := files.Get(f.path); got != f.data { - t.Errorf("%d: expected %q, got %q", i, f.data, got) - } - } -} - -func TestFileGlob(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - matched := f.Glob("story/**") - - as.Len(matched, 2, "Should be two files in glob story/**") - as.Equal("Joseph Conrad", matched.Get("story/author.txt")) -} - -func TestToConfig(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - out := f.Glob("**/captain.txt").AsConfig() - as.Equal("captain.txt: The Captain", out) - - out = f.Glob("ship/**").AsConfig() - as.Equal("captain.txt: The Captain\nstowaway.txt: Legatt", out) -} - -func TestToSecret(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - out := f.Glob("ship/**").AsSecrets() - as.Equal("captain.txt: VGhlIENhcHRhaW4=\nstowaway.txt: TGVnYXR0", out) -} - -func TestLines(t *testing.T) { - as := assert.New(t) - - f := getTestFiles() - - out := f.Lines("multiline/test.txt") - as.Len(out, 2) - - as.Equal("bar", out[0]) -} diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go deleted file mode 100644 index f563b5be..00000000 --- a/pkg/engine/funcs.go +++ /dev/null @@ -1,179 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "bytes" - "encoding/json" - "strings" - "text/template" - - "github.com/BurntSushi/toml" - sprig "github.com/Masterminds/sprig/v3" - "sigs.k8s.io/yaml" -) - -const NotImplementedErrString = "not implemented" - -// funcMap returns a mapping of all of the functions that Engine has. -// -// Because some functions are late-bound (e.g. contain context-sensitive -// data), the functions may not all perform identically outside of an Engine -// as they will inside of an Engine. -// -// Known late-bound functions: -// -// - "include" -// - "tpl" -// -// These are late-bound in Engine.Render(). The -// version included in the FuncMap is a placeholder. -// -func funcMap() template.FuncMap { - f := sprig.TxtFuncMap() - delete(f, "env") - delete(f, "expandenv") - - // Add some extra functionality - extra := template.FuncMap{ - "toToml": toTOML, - "toYaml": toYAML, - "fromYaml": fromYAML, - "fromYamlArray": fromYAMLArray, - "toJson": toJSON, - "fromJson": fromJSON, - "fromJsonArray": fromJSONArray, - - // This is a placeholder for the "include" function, which is - // late-bound to a template. By declaring it here, we preserve the - // integrity of the linter. - "include": func(string, interface{}) string { return NotImplementedErrString }, - "tpl": func(string, interface{}) interface{} { return NotImplementedErrString }, - "required": func(string, interface{}) (interface{}, error) { return NotImplementedErrString, nil }, - // Provide a placeholder for the "lookup" function, which requires a kubernetes - // connection. - "lookup": func(string, string, string, string) (map[string]interface{}, error) { - return map[string]interface{}{}, nil - }, - } - - for k, v := range extra { - f[k] = v - } - - return f -} - -// toYAML takes an interface, marshals it to yaml, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toYAML(v interface{}) string { - data, err := yaml.Marshal(v) - if err != nil { - // Swallow errors inside of a template. - return "" - } - return strings.TrimSuffix(string(data), "\n") -} - -// fromYAML converts a YAML document into a map[string]interface{}. -// -// This is not a general-purpose YAML parser, and will not parse all valid -// YAML documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string into -// m["Error"] in the returned map. -func fromYAML(str string) map[string]interface{} { - m := map[string]interface{}{} - - if err := yaml.Unmarshal([]byte(str), &m); err != nil { - m["Error"] = err.Error() - } - return m -} - -// fromYAMLArray converts a YAML array into a []interface{}. -// -// This is not a general-purpose YAML parser, and will not parse all valid -// YAML documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string as -// the first and only item in the returned array. -func fromYAMLArray(str string) []interface{} { - a := []interface{}{} - - if err := yaml.Unmarshal([]byte(str), &a); err != nil { - a = []interface{}{err.Error()} - } - return a -} - -// toTOML takes an interface, marshals it to toml, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toTOML(v interface{}) string { - b := bytes.NewBuffer(nil) - e := toml.NewEncoder(b) - err := e.Encode(v) - if err != nil { - return err.Error() - } - return b.String() -} - -// toJSON takes an interface, marshals it to json, and returns a string. It will -// always return a string, even on marshal error (empty string). -// -// This is designed to be called from a template. -func toJSON(v interface{}) string { - data, err := json.Marshal(v) - if err != nil { - // Swallow errors inside of a template. - return "" - } - return string(data) -} - -// fromJSON converts a JSON document into a map[string]interface{}. -// -// This is not a general-purpose JSON parser, and will not parse all valid -// JSON documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string into -// m["Error"] in the returned map. -func fromJSON(str string) map[string]interface{} { - m := make(map[string]interface{}) - - if err := json.Unmarshal([]byte(str), &m); err != nil { - m["Error"] = err.Error() - } - return m -} - -// fromJSONArray converts a JSON array into a []interface{}. -// -// This is not a general-purpose JSON parser, and will not parse all valid -// JSON documents. Additionally, because its intended use is within templates -// it tolerates errors. It will insert the returned error message string as -// the first and only item in the returned array. -func fromJSONArray(str string) []interface{} { - a := []interface{}{} - - if err := json.Unmarshal([]byte(str), &a); err != nil { - a = []interface{}{err.Error()} - } - return a -} diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go deleted file mode 100644 index 29bc121b..00000000 --- a/pkg/engine/funcs_test.go +++ /dev/null @@ -1,178 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "strings" - "testing" - "text/template" - - "github.com/stretchr/testify/assert" -) - -func TestFuncs(t *testing.T) { - //TODO write tests for failure cases - tests := []struct { - tpl, expect string - vars interface{} - }{{ - tpl: `{{ toYaml . }}`, - expect: `foo: bar`, - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ toToml . }}`, - expect: "foo = \"bar\"\n", - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ toJson . }}`, - expect: `{"foo":"bar"}`, - vars: map[string]interface{}{"foo": "bar"}, - }, { - tpl: `{{ fromYaml . }}`, - expect: "map[hello:world]", - vars: `hello: world`, - }, { - tpl: `{{ fromYamlArray . }}`, - expect: "[one 2 map[name:helm]]", - vars: "- one\n- 2\n- name: helm\n", - }, { - tpl: `{{ fromYamlArray . }}`, - expect: "[one 2 map[name:helm]]", - vars: `["one", 2, { "name": "helm" }]`, - }, { - // Regression for https://github.com/helm/helm/issues/2271 - tpl: `{{ toToml . }}`, - expect: "[mast]\n sail = \"white\"\n", - vars: map[string]map[string]string{"mast": {"sail": "white"}}, - }, { - tpl: `{{ fromYaml . }}`, - expect: "map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]", - vars: "- one\n- two\n", - }, { - tpl: `{{ fromJson .}}`, - expect: `map[hello:world]`, - vars: `{"hello":"world"}`, - }, { - tpl: `{{ fromJson . }}`, - expect: `map[Error:json: cannot unmarshal array into Go value of type map[string]interface {}]`, - vars: `["one", "two"]`, - }, { - tpl: `{{ fromJsonArray . }}`, - expect: `[one 2 map[name:helm]]`, - vars: `["one", 2, { "name": "helm" }]`, - }, { - tpl: `{{ fromJsonArray . }}`, - expect: `[json: cannot unmarshal object into Go value of type []interface {}]`, - vars: `{"hello": "world"}`, - }, { - tpl: `{{ merge .dict (fromYaml .yaml) }}`, - expect: `map[a:map[b:c]]`, - vars: map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`}, - }, { - tpl: `{{ merge (fromYaml .yaml) .dict }}`, - expect: `map[a:map[b:d]]`, - vars: map[string]interface{}{"dict": map[string]interface{}{"a": map[string]interface{}{"b": "c"}}, "yaml": `{"a":{"b":"d"}}`}, - }, { - tpl: `{{ fromYaml . }}`, - expect: `map[Error:error unmarshaling JSON: while decoding JSON: json: cannot unmarshal array into Go value of type map[string]interface {}]`, - vars: `["one", "two"]`, - }, { - tpl: `{{ fromYamlArray . }}`, - expect: `[error unmarshaling JSON: while decoding JSON: json: cannot unmarshal object into Go value of type []interface {}]`, - vars: `hello: world`, - }, { - // This should never result in a network lookup. Regression for #7955 - tpl: `{{ lookup "v1" "Namespace" "" "unlikelynamespace99999999" }}`, - expect: `map[]`, - vars: `["one", "two"]`, - }} - - for _, tt := range tests { - var b strings.Builder - err := template.Must(template.New("test").Funcs(funcMap()).Parse(tt.tpl)).Execute(&b, tt.vars) - assert.NoError(t, err) - assert.Equal(t, tt.expect, b.String(), tt.tpl) - } -} - -// This test to check a function provided by sprig is due to a change in a -// dependency of sprig. mergo in v0.3.9 changed the way it merges and only does -// public fields (i.e. those starting with a capital letter). This test, from -// sprig, fails in the new version. This is a behavior change for mergo that -// impacts sprig and Helm users. This test will help us to not update to a -// version of mergo (even accidentally) that causes a breaking change. See -// sprig changelog and notes for more details. -// Note, Go modules assume semver is never broken. So, there is no way to tell -// the tooling to not update to a minor or patch version. `go install` could -// be used to accidentally update mergo. This test and message should catch -// the problem and explain why it's happening. -func TestMerge(t *testing.T) { - dict := map[string]interface{}{ - "src2": map[string]interface{}{ - "h": 10, - "i": "i", - "j": "j", - }, - "src1": map[string]interface{}{ - "a": 1, - "b": 2, - "d": map[string]interface{}{ - "e": "four", - }, - "g": []int{6, 7}, - "i": "aye", - "j": "jay", - "k": map[string]interface{}{ - "l": false, - }, - }, - "dst": map[string]interface{}{ - "a": "one", - "c": 3, - "d": map[string]interface{}{ - "f": 5, - }, - "g": []int{8, 9}, - "i": "eye", - "k": map[string]interface{}{ - "l": true, - }, - }, - } - tpl := `{{merge .dst .src1 .src2}}` - var b strings.Builder - err := template.Must(template.New("test").Funcs(funcMap()).Parse(tpl)).Execute(&b, dict) - assert.NoError(t, err) - - expected := map[string]interface{}{ - "a": "one", // key overridden - "b": 2, // merged from src1 - "c": 3, // merged from dst - "d": map[string]interface{}{ // deep merge - "e": "four", - "f": 5, - }, - "g": []int{8, 9}, // overridden - arrays are not merged - "h": 10, // merged from src2 - "i": "eye", // overridden twice - "j": "jay", // overridden and merged - "k": map[string]interface{}{ - "l": true, // overridden - }, - } - assert.Equal(t, expected, dict["dst"]) -} diff --git a/pkg/engine/lookup_func.go b/pkg/engine/lookup_func.go deleted file mode 100644 index d1bf1105..00000000 --- a/pkg/engine/lookup_func.go +++ /dev/null @@ -1,124 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package engine - -import ( - "context" - "log" - "strings" - - "github.com/pkg/errors" - apierrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/discovery" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" -) - -type lookupFunc = func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) - -// NewLookupFunction returns a function for looking up objects in the cluster. -// -// If the resource does not exist, no error is raised. -// -// This function is considered deprecated, and will be renamed in Helm 4. It will no -// longer be a public function. -func NewLookupFunction(config *rest.Config) lookupFunc { - return func(apiversion string, resource string, namespace string, name string) (map[string]interface{}, error) { - var client dynamic.ResourceInterface - c, namespaced, err := getDynamicClientOnKind(apiversion, resource, config) - if err != nil { - return map[string]interface{}{}, err - } - if namespaced && namespace != "" { - client = c.Namespace(namespace) - } else { - client = c - } - if name != "" { - // this will return a single object - obj, err := client.Get(context.Background(), name, metav1.GetOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - // Just return an empty interface when the object was not found. - // That way, users can use `if not (lookup ...)` in their templates. - return map[string]interface{}{}, nil - } - return map[string]interface{}{}, err - } - return obj.UnstructuredContent(), nil - } - // this will return a list - obj, err := client.List(context.Background(), metav1.ListOptions{}) - if err != nil { - if apierrors.IsNotFound(err) { - // Just return an empty interface when the object was not found. - // That way, users can use `if not (lookup ...)` in their templates. - return map[string]interface{}{}, nil - } - return map[string]interface{}{}, err - } - return obj.UnstructuredContent(), nil - } -} - -// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced. -func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) { - gvk := schema.FromAPIVersionAndKind(apiversion, kind) - apiRes, err := getAPIResourceForGVK(gvk, config) - if err != nil { - log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err) - return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String()) - } - gvr := schema.GroupVersionResource{ - Group: apiRes.Group, - Version: apiRes.Version, - Resource: apiRes.Name, - } - intf, err := dynamic.NewForConfig(config) - if err != nil { - log.Printf("[ERROR] unable to get dynamic client %s", err) - return nil, false, err - } - res := intf.Resource(gvr) - return res, apiRes.Namespaced, nil -} - -func getAPIResourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) { - res := metav1.APIResource{} - discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) - if err != nil { - log.Printf("[ERROR] unable to create discovery client %s", err) - return res, err - } - resList, err := discoveryClient.ServerResourcesForGroupVersion(gvk.GroupVersion().String()) - if err != nil { - log.Printf("[ERROR] unable to retrieve resource list for: %s , error: %s", gvk.GroupVersion().String(), err) - return res, err - } - for _, resource := range resList.APIResources { - // if a resource contains a "/" it's referencing a subresource. we don't support suberesource for now. - if resource.Kind == gvk.Kind && !strings.Contains(resource.Name, "/") { - res = resource - res.Group = gvk.Group - res.Version = gvk.Version - break - } - } - return res, nil -} diff --git a/pkg/lint/lint.go b/pkg/lint/lint.go deleted file mode 100644 index ce671c70..00000000 --- a/pkg/lint/lint.go +++ /dev/null @@ -1,37 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package lint - -import ( - "path/filepath" - - "github.com/mrjosh/helm-ls/pkg/lint/rules" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// All runs all of the available linters on the given base directory. -func All(basedir string, values map[string]interface{}, namespace string, strict bool) support.Linter { - // Using abs path to get directory context - chartDir, _ := filepath.Abs(basedir) - - linter := support.Linter{ChartDir: chartDir} - //rules.Chartfile(&linter) - //rules.ValuesWithOverrides(&linter, values) - rules.Templates(&linter, values, namespace, strict) - //rules.Dependencies(&linter) - return linter -} diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go deleted file mode 100644 index 823d9889..00000000 --- a/pkg/lint/rules/chartfile.go +++ /dev/null @@ -1,209 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "fmt" - "os" - "path/filepath" - - semver "github.com/Masterminds/semver/v3" - "github.com/asaskevich/govalidator" - "github.com/pkg/errors" - "sigs.k8s.io/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Chartfile runs a set of linter rules related to Chart.yaml file -func Chartfile(linter *support.Linter) { - chartFileName := "Chart.yaml" - chartPath := filepath.Join(linter.ChartDir, chartFileName) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlNotDirectory(chartPath)) - - chartFile, err := chartutil.LoadChartfile(chartPath) - validChartFile := linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartYamlFormat(err)) - - // Guard clause. Following linter rules require a parsable ChartFile - if !validChartFile { - return - } - - // type check for Chart.yaml . ignoring error as any parse - // errors would already be caught in the above load function - chartFileForTypeCheck, _ := loadChartFileForTypeCheck(chartPath) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) - - // Chart metadata - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) - - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersionType(chartFileForTypeCheck)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAppVersionType(chartFileForTypeCheck)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartSources(chartFile)) - linter.RunLinterRule(support.InfoSev, chartFileName, validateChartIconPresence(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartIconURL(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartType(chartFile)) - linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartDependencies(chartFile)) -} - -func validateChartVersionType(data map[string]interface{}) error { - return isStringValue(data, "version") -} - -func validateChartAppVersionType(data map[string]interface{}) error { - return isStringValue(data, "appVersion") -} - -func isStringValue(data map[string]interface{}, key string) error { - value, ok := data[key] - if !ok { - return nil - } - valueType := fmt.Sprintf("%T", value) - if valueType != "string" { - return errors.Errorf("%s should be of type string but it's of type %s", key, valueType) - } - return nil -} - -func validateChartYamlNotDirectory(chartPath string) error { - fi, err := os.Stat(chartPath) - - if err == nil && fi.IsDir() { - return errors.New("should be a file, not a directory") - } - return nil -} - -func validateChartYamlFormat(chartFileError error) error { - if chartFileError != nil { - return errors.Errorf("unable to parse YAML\n\t%s", chartFileError.Error()) - } - return nil -} - -func validateChartName(cf *chart.Metadata) error { - if cf.Name == "" { - return errors.New("name is required") - } - return nil -} - -func validateChartAPIVersion(cf *chart.Metadata) error { - if cf.APIVersion == "" { - return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"") - } - - if cf.APIVersion != chart.APIVersionV1 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("apiVersion '%s' is not valid. The value must be either \"v1\" or \"v2\"", cf.APIVersion) - } - - return nil -} - -func validateChartVersion(cf *chart.Metadata) error { - if cf.Version == "" { - return errors.New("version is required") - } - - version, err := semver.NewVersion(cf.Version) - - if err != nil { - return errors.Errorf("version '%s' is not a valid SemVer", cf.Version) - } - - c, err := semver.NewConstraint(">0.0.0-0") - if err != nil { - return err - } - valid, msg := c.Validate(version) - - if !valid && len(msg) > 0 { - return errors.Errorf("version %v", msg[0]) - } - - return nil -} - -func validateChartMaintainer(cf *chart.Metadata) error { - for _, maintainer := range cf.Maintainers { - if maintainer.Name == "" { - return errors.New("each maintainer requires a name") - } else if maintainer.Email != "" && !govalidator.IsEmail(maintainer.Email) { - return errors.Errorf("invalid email '%s' for maintainer '%s'", maintainer.Email, maintainer.Name) - } else if maintainer.URL != "" && !govalidator.IsURL(maintainer.URL) { - return errors.Errorf("invalid url '%s' for maintainer '%s'", maintainer.URL, maintainer.Name) - } - } - return nil -} - -func validateChartSources(cf *chart.Metadata) error { - for _, source := range cf.Sources { - if source == "" || !govalidator.IsRequestURL(source) { - return errors.Errorf("invalid source URL '%s'", source) - } - } - return nil -} - -func validateChartIconPresence(cf *chart.Metadata) error { - if cf.Icon == "" { - return errors.New("icon is recommended") - } - return nil -} - -func validateChartIconURL(cf *chart.Metadata) error { - if cf.Icon != "" && !govalidator.IsRequestURL(cf.Icon) { - return errors.Errorf("invalid icon URL '%s'", cf.Icon) - } - return nil -} - -func validateChartDependencies(cf *chart.Metadata) error { - if len(cf.Dependencies) > 0 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("dependencies are not valid in the Chart file with apiVersion '%s'. They are valid in apiVersion '%s'", cf.APIVersion, chart.APIVersionV2) - } - return nil -} - -func validateChartType(cf *chart.Metadata) error { - if len(cf.Type) > 0 && cf.APIVersion != chart.APIVersionV2 { - return fmt.Errorf("chart type is not valid in apiVersion '%s'. It is valid in apiVersion '%s'", cf.APIVersion, chart.APIVersionV2) - } - return nil -} - -// loadChartFileForTypeCheck loads the Chart.yaml -// in a generic form of a map[string]interface{}, so that the type -// of the values can be checked -func loadChartFileForTypeCheck(filename string) (map[string]interface{}, error) { - b, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - y := make(map[string]interface{}) - err = yaml.Unmarshal(b, &y) - return y, err -} diff --git a/pkg/lint/rules/dependencies.go b/pkg/lint/rules/dependencies.go deleted file mode 100644 index 1bf37f7c..00000000 --- a/pkg/lint/rules/dependencies.go +++ /dev/null @@ -1,82 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "fmt" - "strings" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chart" - "github.com/mrjosh/helm-ls/pkg/chart/loader" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Dependencies runs lints against a chart's dependencies -// -// See https://github.com/helm/helm/issues/7910 -func Dependencies(linter *support.Linter) { - c, err := loader.LoadDir(linter.ChartDir) - if !linter.RunLinterRule(support.ErrorSev, "", validateChartFormat(err)) { - return - } - - linter.RunLinterRule(support.ErrorSev, linter.ChartDir, validateDependencyInMetadata(c)) - linter.RunLinterRule(support.WarningSev, linter.ChartDir, validateDependencyInChartsDir(c)) -} - -func validateChartFormat(chartError error) error { - if chartError != nil { - return errors.Errorf("unable to load chart\n\t%s", chartError) - } - return nil -} - -func validateDependencyInChartsDir(c *chart.Chart) (err error) { - dependencies := map[string]struct{}{} - missing := []string{} - for _, dep := range c.Dependencies() { - dependencies[dep.Metadata.Name] = struct{}{} - } - for _, dep := range c.Metadata.Dependencies { - if _, ok := dependencies[dep.Name]; !ok { - missing = append(missing, dep.Name) - } - } - if len(missing) > 0 { - err = fmt.Errorf("chart directory is missing these dependencies: %s", strings.Join(missing, ",")) - } - return err -} - -func validateDependencyInMetadata(c *chart.Chart) (err error) { - dependencies := map[string]struct{}{} - missing := []string{} - for _, dep := range c.Metadata.Dependencies { - dependencies[dep.Name] = struct{}{} - } - for _, dep := range c.Dependencies() { - if _, ok := dependencies[dep.Metadata.Name]; !ok { - missing = append(missing, dep.Metadata.Name) - } - } - if len(missing) > 0 { - err = fmt.Errorf("chart metadata is missing these dependencies: %s", strings.Join(missing, ",")) - } - return err -} diff --git a/pkg/lint/rules/deprecations.go b/pkg/lint/rules/deprecations.go deleted file mode 100644 index 784e3a3d..00000000 --- a/pkg/lint/rules/deprecations.go +++ /dev/null @@ -1,98 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "fmt" - "strconv" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apiserver/pkg/endpoints/deprecation" - kscheme "k8s.io/client-go/kubernetes/scheme" -) - -var ( - // This should be set in the Makefile based on the version of client-go being imported. - // These constants will be overwritten with LDFLAGS. The version components must be - // strings in order for LDFLAGS to set them. - k8sVersionMajor = "1" - k8sVersionMinor = "20" -) - -// deprecatedAPIError indicates than an API is deprecated in Kubernetes -type deprecatedAPIError struct { - Deprecated string - Message string -} - -func (e deprecatedAPIError) Error() string { - msg := e.Message - return msg -} - -func validateNoDeprecations(resource *K8sYamlStruct) error { - // if `resource` does not have an APIVersion or Kind, we cannot test it for deprecation - if resource.APIVersion == "" { - return nil - } - if resource.Kind == "" { - return nil - } - - runtimeObject, err := resourceToRuntimeObject(resource) - if err != nil { - // do not error for non-kubernetes resources - if runtime.IsNotRegisteredError(err) { - return nil - } - return err - } - maj, err := strconv.Atoi(k8sVersionMajor) - if err != nil { - return err - } - min, err := strconv.Atoi(k8sVersionMinor) - if err != nil { - return err - } - - if !deprecation.IsDeprecated(runtimeObject, maj, min) { - return nil - } - gvk := fmt.Sprintf("%s %s", resource.APIVersion, resource.Kind) - return deprecatedAPIError{ - Deprecated: gvk, - Message: deprecation.WarningMessage(runtimeObject), - } -} - -func resourceToRuntimeObject(resource *K8sYamlStruct) (runtime.Object, error) { - scheme := runtime.NewScheme() - - if err := kscheme.AddToScheme(scheme); err != nil { - return nil, err - } - - gvk := schema.FromAPIVersionAndKind(resource.APIVersion, resource.Kind) - out, err := scheme.New(gvk) - if err != nil { - return nil, err - } - out.GetObjectKind().SetGroupVersionKind(gvk) - return out, nil -} diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go deleted file mode 100644 index 61ed6b9f..00000000 --- a/pkg/lint/rules/template.go +++ /dev/null @@ -1,385 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "bufio" - "bytes" - "fmt" - "io" - "os" - "path" - "path/filepath" - "regexp" - "strconv" - "strings" - - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/api/validation" - apipath "k8s.io/apimachinery/pkg/api/validation/path" - "k8s.io/apimachinery/pkg/util/validation/field" - "k8s.io/apimachinery/pkg/util/yaml" - - "github.com/mrjosh/helm-ls/pkg/chart/loader" - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/engine" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -var ( - crdHookSearch = regexp.MustCompile(`"?helm\.sh/hook"?:\s+crd-install`) - releaseTimeSearch = regexp.MustCompile(`\.Release\.Time`) -) - -// Templates lints the templates in the Linter. -func Templates(linter *support.Linter, values map[string]interface{}, namespace string, _ bool) { - fpath := "templates/" - templatesPath := filepath.Join(linter.ChartDir, fpath) - - templatesDirExist := linter.RunLinterRule(support.WarningSev, fpath, validateTemplatesDir(templatesPath)) - - // Templates directory is optional for now - if !templatesDirExist { - return - } - - // Load chart and parse templates - chart, err := loader.Load(linter.ChartDir) - - _ = linter.RunLinterRule(support.ErrorSev, fpath, err) - //if !chartLoaded { - // ignoring this error and continuing with lint - //} - - options := chartutil.ReleaseOptions{ - Name: "test-release", - Namespace: namespace, - } - - // lint ignores import-values - // See https://github.com/helm/helm/issues/9658 - if err := chartutil.ProcessDependencies(chart, values); err != nil { - return - } - - cvals, err := chartutil.CoalesceValues(chart, values) - if err != nil { - return - } - valuesToRender, err := chartutil.ToRenderValues(chart, cvals, options, nil) - if err != nil { - linter.RunLinterRule(support.ErrorSev, fpath, err) - return - } - var e engine.Engine - e.LintMode = true - renderedContentMap, err := e.Render(chart, valuesToRender) - - renderOk := linter.RunLinterRule(support.ErrorSev, fpath, err) - - if !renderOk { - return - } - - /* Iterate over all the templates to check: - - It is a .yaml file - - All the values in the template file is defined - - {{}} include | quote - - Generated content is a valid Yaml file - - Metadata.Namespace is not set - */ - for _, template := range chart.Templates { - fileName, data := template.Name, template.Data - fpath = fileName - - linter.RunLinterRule(support.ErrorSev, fpath, validateAllowedExtension(fileName)) - // These are v3 specific checks to make sure and warn people if their - // chart is not compatible with v3 - linter.RunLinterRule(support.WarningSev, fpath, validateNoCRDHooks(data)) - linter.RunLinterRule(support.ErrorSev, fpath, validateNoReleaseTime(data)) - - // We only apply the following lint rules to yaml files - if filepath.Ext(fileName) != ".yaml" || filepath.Ext(fileName) == ".yml" { - continue - } - - // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1463 - // Check that all the templates have a matching value - // linter.RunLinterRule(support.WarningSev, fpath, validateNoMissingValues(templatesPath, valuesToRender, preExecutedTemplate)) - - // NOTE: disabled for now, Refs https://github.com/helm/helm/issues/1037 - // linter.RunLinterRule(support.WarningSev, fpath, validateQuotes(string(preExecutedTemplate))) - - renderedContent := renderedContentMap[path.Join(chart.Name(), fileName)] - if strings.TrimSpace(renderedContent) != "" { - linter.RunLinterRule(support.WarningSev, fpath, validateTopIndentLevel(renderedContent)) - - decoder := yaml.NewYAMLOrJSONDecoder(strings.NewReader(renderedContent), 4096) - - // Lint all resources if the file contains multiple documents separated by --- - for { - // Even though K8sYamlStruct only defines a few fields, an error in any other - // key will be raised as well - var yamlStruct *K8sYamlStruct - - err := decoder.Decode(&yamlStruct) - if err == io.EOF { - break - } - - // If YAML linting fails, we sill progress. So we don't capture the returned state - // on this linter run. - linter.RunLinterRule(support.ErrorSev, fpath, validateYamlContent(err)) - - if yamlStruct != nil { - // NOTE: set to warnings to allow users to support out-of-date kubernetes - // Refs https://github.com/helm/helm/issues/8596 - linter.RunLinterRule(support.WarningSev, fpath, validateMetadataName(yamlStruct)) - linter.RunLinterRule(support.WarningSev, fpath, validateNoDeprecations(yamlStruct)) - - linter.RunLinterRule(support.ErrorSev, fpath, validateMatchSelector(yamlStruct, renderedContent)) - linter.RunLinterRule(support.ErrorSev, fpath, validateListAnnotations(yamlStruct, renderedContent)) - } - } - } - } -} - -// validateTopIndentLevel checks that the content does not start with an indent level > 0. -// -// This error can occur when a template accidentally inserts space. It can cause -// unpredictable errors depending on whether the text is normalized before being passed -// into the YAML parser. So we trap it here. -// -// See https://github.com/helm/helm/issues/8467 -func validateTopIndentLevel(content string) error { - // Read lines until we get to a non-empty one - scanner := bufio.NewScanner(bytes.NewBufferString(content)) - for scanner.Scan() { - line := scanner.Text() - // If line is empty, skip - if strings.TrimSpace(line) == "" { - continue - } - // If it starts with one or more spaces, this is an error - if strings.HasPrefix(line, " ") || strings.HasPrefix(line, "\t") { - return fmt.Errorf("document starts with an illegal indent: %q, which may cause parsing problems", line) - } - // Any other condition passes. - return nil - } - return scanner.Err() -} - -// Validation functions -func validateTemplatesDir(templatesPath string) error { - if fi, err := os.Stat(templatesPath); err != nil { - return errors.New("directory not found") - } else if !fi.IsDir() { - return errors.New("not a directory") - } - return nil -} - -func validateAllowedExtension(fileName string) error { - ext := filepath.Ext(fileName) - validExtensions := []string{".yaml", ".yml", ".tpl", ".txt"} - - for _, b := range validExtensions { - if b == ext { - return nil - } - } - - return errors.Errorf("file extension '%s' not valid. Valid extensions are .yaml, .yml, .tpl, or .txt", ext) -} - -type YAMLToJSONParseError struct { - message string - Line int -} - -func (err *YAMLToJSONParseError) Error() string { - return err.message -} - -func validateYamlContent(err error) error { - er, ok := err.(yaml.YAMLSyntaxError) - if ok { - - erStr := strings.ReplaceAll( - er.Error(), - "error converting YAML to JSON: yaml:", - "", - ) - - cleanStr := strings.TrimSpace(erStr) - - splittedErr := strings.Split(cleanStr, ":") - linenostr := strings.Split(splittedErr[0], " ") - - lineno, _ := strconv.Atoi(linenostr[1]) - - return &YAMLToJSONParseError{ - message: strings.TrimSpace(splittedErr[1]), - Line: lineno, - } - } - return errors.Wrap(err, "unable to parse YAML") -} - -type MetadataError struct { - message string - details error -} - -func (err *MetadataError) Error() string { - return err.message -} - -func (err *MetadataError) Details() error { - return err.details -} - -// validateMetadataName uses the correct validation function for the object -// Kind, or if not set, defaults to the standard definition of a subdomain in -// DNS (RFC 1123), used by most resources. -func validateMetadataName(obj *K8sYamlStruct) error { - fn := validateMetadataNameFunc(obj) - allErrs := field.ErrorList{} - for _, msg := range fn(obj.Metadata.Name, false) { - allErrs = append(allErrs, field.Invalid(field.NewPath("metadata").Child("name"), obj.Metadata.Name, msg)) - } - if len(allErrs) > 0 { - return &MetadataError{ - message: "object name does not conform to Kubernetes naming requirements", - details: errors.Wrapf(allErrs.ToAggregate(), "object name does not conform to Kubernetes naming requirements: %q", obj.Metadata.Name), - } - } - return nil -} - -// validateMetadataNameFunc will return a name validation function for the -// object kind, if defined below. -// -// Rules should match those set in the various api validations: -// https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/core/validation/validation.go#L205-L274 -// https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/apps/validation/validation.go#L39 -// ... -// -// Implementing here to avoid importing k/k. -// -// If no mapping is defined, returns NameIsDNSSubdomain. This is used by object -// kinds that don't have special requirements, so is the most likely to work if -// new kinds are added. -func validateMetadataNameFunc(obj *K8sYamlStruct) validation.ValidateNameFunc { - switch strings.ToLower(obj.Kind) { - case "pod", "node", "secret", "endpoints", "resourcequota", // core - "controllerrevision", "daemonset", "deployment", "replicaset", "statefulset", // apps - "autoscaler", // autoscaler - "cronjob", "job", // batch - "lease", // coordination - "endpointslice", // discovery - "networkpolicy", "ingress", // networking - "podsecuritypolicy", // policy - "priorityclass", // scheduling - "podpreset", // settings - "storageclass", "volumeattachment", "csinode": // storage - return validation.NameIsDNSSubdomain - case "service": - return validation.NameIsDNS1035Label - case "namespace": - return validation.ValidateNamespaceName - case "serviceaccount": - return validation.ValidateServiceAccountName - case "certificatesigningrequest": - // No validation. - // https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/certificates/validation/validation.go#L137-L140 - return func(name string, prefix bool) []string { return nil } - case "role", "clusterrole", "rolebinding", "clusterrolebinding": - // https://github.com/kubernetes/kubernetes/blob/v1.20.0/pkg/apis/rbac/validation/validation.go#L32-L34 - return func(name string, prefix bool) []string { - return apipath.IsValidPathSegmentName(name) - } - default: - return validation.NameIsDNSSubdomain - } -} - -func validateNoCRDHooks(manifest []byte) error { - if crdHookSearch.Match(manifest) { - return errors.New("manifest is a crd-install hook. This hook is no longer supported in v3 and all CRDs should also exist the crds/ directory at the top level of the chart") - } - return nil -} - -func validateNoReleaseTime(manifest []byte) error { - if releaseTimeSearch.Match(manifest) { - return errors.New(".Release.Time has been removed in v3, please replace with the `now` function in your templates") - } - return nil -} - -// validateMatchSelector ensures that template specs have a selector declared. -// See https://github.com/helm/helm/issues/1990 -func validateMatchSelector(yamlStruct *K8sYamlStruct, manifest string) error { - switch yamlStruct.Kind { - case "Deployment", "ReplicaSet", "DaemonSet", "StatefulSet": - // verify that matchLabels or matchExpressions is present - if !(strings.Contains(manifest, "matchLabels") || strings.Contains(manifest, "matchExpressions")) { - return fmt.Errorf("a %s must contain matchLabels or matchExpressions, and %q does not", yamlStruct.Kind, yamlStruct.Metadata.Name) - } - } - return nil -} -func validateListAnnotations(yamlStruct *K8sYamlStruct, manifest string) error { - if yamlStruct.Kind == "List" { - m := struct { - Items []struct { - Metadata struct { - Annotations map[string]string - } - } - }{} - - if err := yaml.Unmarshal([]byte(manifest), &m); err != nil { - return validateYamlContent(err) - } - - for _, i := range m.Items { - if _, ok := i.Metadata.Annotations["helm.sh/resource-policy"]; ok { - return errors.New("Annotation 'helm.sh/resource-policy' within List objects are ignored") - } - } - } - return nil -} - -// K8sYamlStruct stubs a Kubernetes YAML file. -// -// DEPRECATED: In Helm 4, this will be made a private type, as it is for use only within -// the rules package. -type K8sYamlStruct struct { - APIVersion string `json:"apiVersion"` - Kind string - Metadata k8sYamlMetadata -} - -type k8sYamlMetadata struct { - Namespace string - Name string -} diff --git a/pkg/lint/rules/values.go b/pkg/lint/rules/values.go deleted file mode 100644 index 843df159..00000000 --- a/pkg/lint/rules/values.go +++ /dev/null @@ -1,86 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package rules - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - - "github.com/mrjosh/helm-ls/pkg/chartutil" - "github.com/mrjosh/helm-ls/pkg/lint/support" -) - -// Values lints a chart's values.yaml file. -// -// This function is deprecated and will be removed in Helm 4. -func Values(linter *support.Linter) { - ValuesWithOverrides(linter, map[string]interface{}{}) -} - -// ValuesWithOverrides tests the values.yaml file. -// -// If a schema is present in the chart, values are tested against that. Otherwise, -// they are only tested for well-formedness. -// -// If additional values are supplied, they are coalesced into the values in values.yaml. -func ValuesWithOverrides(linter *support.Linter, values map[string]interface{}) { - file := "values.yaml" - vf := filepath.Join(linter.ChartDir, file) - fileExists := linter.RunLinterRule(support.InfoSev, file, validateValuesFileExistence(vf)) - - if !fileExists { - return - } - - linter.RunLinterRule(support.ErrorSev, file, validateValuesFile(vf, values)) -} - -func validateValuesFileExistence(valuesPath string) error { - _, err := os.Stat(valuesPath) - if err != nil { - return errors.Errorf("file does not exist") - } - return nil -} - -func validateValuesFile(valuesPath string, overrides map[string]interface{}) error { - values, err := chartutil.ReadValuesFile(valuesPath) - if err != nil { - return errors.Wrap(err, "unable to parse YAML") - } - - // Helm 3.0.0 carried over the values linting from Helm 2.x, which only tests the top - // level values against the top-level expectations. Subchart values are not linted. - // We could change that. For now, though, we retain that strategy, and thus can - // coalesce tables (like reuse-values does) instead of doing the full chart - // CoalesceValues - coalescedValues := chartutil.CoalesceTables(make(map[string]interface{}, len(overrides)), overrides) - coalescedValues = chartutil.CoalesceTables(coalescedValues, values) - - ext := filepath.Ext(valuesPath) - schemaPath := valuesPath[:len(valuesPath)-len(ext)] + ".schema.json" - schema, err := os.ReadFile(schemaPath) - if len(schema) == 0 { - return nil - } - if err != nil { - return err - } - return chartutil.ValidateAgainstSingleSchema(coalescedValues, schema) -} diff --git a/pkg/lint/support/error.go b/pkg/lint/support/error.go deleted file mode 100644 index 17d5ef25..00000000 --- a/pkg/lint/support/error.go +++ /dev/null @@ -1 +0,0 @@ -package support diff --git a/pkg/lint/support/message.go b/pkg/lint/support/message.go deleted file mode 100644 index 1f22418e..00000000 --- a/pkg/lint/support/message.go +++ /dev/null @@ -1,97 +0,0 @@ -/* -Copyright The Helm Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package support - -import ( - "fmt" - - "github.com/mrjosh/helm-ls/pkg/engine" -) - -// Severity indicates the severity of a Message. -const ( - // UnknownSev indicates that the severity of the error is unknown, and should not stop processing. - UnknownSev = iota - // InfoSev indicates information, for example missing values.yaml file - InfoSev - // WarningSev indicates that something does not meet code standards, but will likely function. - WarningSev - // ErrorSev indicates that something will not likely function. - ErrorSev -) - -// sev matches the *Sev states. -var sev = []string{"UNKNOWN", "INFO", "WARNING", "ERROR"} - -// Linter encapsulates a linting run of a particular chart. -type Linter struct { - Messages []Message - // The highest severity of all the failing lint rules - HighestSeverity int - ChartDir string -} - -// Message describes an error encountered while linting. -type Message struct { - // Severity is one of the *Sev constants - Severity int - Path string - Err error -} - -func (m Message) Error() string { - return fmt.Sprintf("[%s] %s: %s", sev[m.Severity], m.Path, m.Err.Error()) -} - -// NewMessage creates a new Message struct -func NewMessage(severity int, path string, err error) Message { - return Message{Severity: severity, Path: path, Err: err} -} - -// RunLinterRule returns true if the validation passed -func (l *Linter) RunLinterRule(severity int, path string, err error) bool { - // severity is out of bound - if severity < 0 || severity >= len(sev) { - return false - } - - if err != nil { - - errs, ok := err.(*engine.LintError) - if ok { - - for _, er := range errs.Errors() { - l.Messages = append(l.Messages, NewMessage(severity, path, er)) - if severity > l.HighestSeverity { - l.HighestSeverity = severity - } - } - - } else { - - l.Messages = append(l.Messages, NewMessage(severity, path, err)) - - if severity > l.HighestSeverity { - l.HighestSeverity = severity - } - } - - } - - return err == nil - -} diff --git a/testdata/example/templates/lint.yaml b/testdata/example/templates/lint.yaml new file mode 100644 index 00000000..07ce5793 --- /dev/null +++ b/testdata/example/templates/lint.yaml @@ -0,0 +1 @@ +{{ end }}