refactor(helm): helm binary to sdk refactor [r8s-229] (#463)
Co-authored-by: stevensbkang <skan070@gmail.com>release/2.28.0
parent
0d25f3f430
commit
b5961d79f8
|
@ -49,6 +49,7 @@ import (
|
|||
"github.com/portainer/portainer/pkg/build"
|
||||
"github.com/portainer/portainer/pkg/featureflags"
|
||||
"github.com/portainer/portainer/pkg/libhelm"
|
||||
libhelmtypes "github.com/portainer/portainer/pkg/libhelm/types"
|
||||
"github.com/portainer/portainer/pkg/libstack/compose"
|
||||
|
||||
"github.com/gofrs/uuid"
|
||||
|
@ -169,8 +170,8 @@ func initKubernetesDeployer(kubernetesTokenCacheManager *kubeproxy.TokenCacheMan
|
|||
return exec.NewKubernetesDeployer(kubernetesTokenCacheManager, kubernetesClientFactory, dataStore, reverseTunnelService, signatureService, proxyManager, assetsPath)
|
||||
}
|
||||
|
||||
func initHelmPackageManager(assetsPath string) (libhelm.HelmPackageManager, error) {
|
||||
return libhelm.NewHelmPackageManager(libhelm.HelmConfig{BinaryPath: assetsPath})
|
||||
func initHelmPackageManager() (libhelmtypes.HelmPackageManager, error) {
|
||||
return libhelm.NewHelmPackageManager()
|
||||
}
|
||||
|
||||
func initAPIKeyService(datastore dataservices.DataStore) apikey.APIKeyService {
|
||||
|
@ -437,7 +438,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
|
|||
|
||||
proxyManager.NewProxyFactory(dataStore, signatureService, reverseTunnelService, dockerClientFactory, kubernetesClientFactory, kubernetesTokenCacheManager, gitService, snapshotService)
|
||||
|
||||
helmPackageManager, err := initHelmPackageManager(*flags.Assets)
|
||||
helmPackageManager, err := initHelmPackageManager()
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("failed initializing helm package manager")
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"InternalAuthSettings": {"RequiredPasswordLength": 12}"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://kubernetes.github.io/ingress-nginx","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
|
||||
jsonobject = `{"LogoURL":"","BlackListedLabels":[],"AuthenticationMethod":1,"InternalAuthSettings": {"RequiredPasswordLength": 12}"LDAPSettings":{"AnonymousMode":true,"ReaderDN":"","URL":"","TLSConfig":{"TLS":false,"TLSSkipVerify":false},"StartTLS":false,"SearchSettings":[{"BaseDN":"","Filter":"","UserNameAttribute":""}],"GroupSearchSettings":[{"GroupBaseDN":"","GroupFilter":"","GroupAttribute":""}],"AutoCreateUsers":true},"OAuthSettings":{"ClientID":"","AccessTokenURI":"","AuthorizationURI":"","ResourceURI":"","RedirectURI":"","UserIdentifier":"","Scopes":"","OAuthAutoCreateUsers":false,"DefaultTeamID":0,"SSO":true,"LogoutURI":"","KubeSecretKey":"j0zLVtY/lAWBk62ByyF0uP80SOXaitsABP0TTJX8MhI="},"OpenAMTConfiguration":{"Enabled":false,"MPSServer":"","MPSUser":"","MPSPassword":"","MPSToken":"","CertFileContent":"","CertFileName":"","CertFilePassword":"","DomainName":""},"FeatureFlagSettings":{},"SnapshotInterval":"5m","TemplatesURL":"https://raw.githubusercontent.com/portainer/templates/master/templates-2.0.json","EdgeAgentCheckinInterval":5,"EnableEdgeComputeFeatures":false,"UserSessionTimeout":"8h","KubeconfigExpiry":"0","EnableTelemetry":true,"HelmRepositoryURL":"https://charts.bitnami.com/bitnami","KubectlShellImage":"portainer/kubectl-shell","DisplayDonationHeader":false,"DisplayExternalContributors":false,"EnableHostManagementFeatures":false,"AllowVolumeBrowserForRegularUsers":false,"AllowBindMountsForRegularUsers":false,"AllowPrivilegedModeForRegularUsers":false,"AllowHostNamespaceForRegularUsers":false,"AllowStackManagementForRegularUsers":false,"AllowDeviceMappingForRegularUsers":false,"AllowContainerCapabilitiesForRegularUsers":false}`
|
||||
passphrase = "my secret key"
|
||||
)
|
||||
|
||||
|
|
|
@ -605,7 +605,7 @@
|
|||
"GlobalDeploymentOptions": {
|
||||
"hideStacksFunctionality": false
|
||||
},
|
||||
"HelmRepositoryURL": "",
|
||||
"HelmRepositoryURL": "https://charts.bitnami.com/bitnami",
|
||||
"InternalAuthSettings": {
|
||||
"RequiredPasswordLength": 12
|
||||
},
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
portainer "github.com/portainer/portainer/api"
|
||||
|
@ -8,8 +9,8 @@ import (
|
|||
"github.com/portainer/portainer/api/http/middlewares"
|
||||
"github.com/portainer/portainer/api/http/security"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
libhelmtypes "github.com/portainer/portainer/pkg/libhelm/types"
|
||||
httperror "github.com/portainer/portainer/pkg/libhttp/error"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -23,11 +24,11 @@ type Handler struct {
|
|||
jwtService portainer.JWTService
|
||||
kubeClusterAccessService kubernetes.KubeClusterAccessService
|
||||
kubernetesDeployer portainer.KubernetesDeployer
|
||||
helmPackageManager libhelm.HelmPackageManager
|
||||
helmPackageManager libhelmtypes.HelmPackageManager
|
||||
}
|
||||
|
||||
// NewHandler creates a handler to manage endpoint group operations.
|
||||
func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStore, jwtService portainer.JWTService, kubernetesDeployer portainer.KubernetesDeployer, helmPackageManager libhelm.HelmPackageManager, kubeClusterAccessService kubernetes.KubeClusterAccessService) *Handler {
|
||||
func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStore, jwtService portainer.JWTService, kubernetesDeployer portainer.KubernetesDeployer, helmPackageManager libhelmtypes.HelmPackageManager, kubeClusterAccessService kubernetes.KubeClusterAccessService) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
requestBouncer: bouncer,
|
||||
|
@ -57,7 +58,7 @@ func NewHandler(bouncer security.BouncerService, dataStore dataservices.DataStor
|
|||
}
|
||||
|
||||
// NewTemplateHandler creates a template handler to manage environment(endpoint) group operations.
|
||||
func NewTemplateHandler(bouncer security.BouncerService, helmPackageManager libhelm.HelmPackageManager) *Handler {
|
||||
func NewTemplateHandler(bouncer security.BouncerService, helmPackageManager libhelmtypes.HelmPackageManager) *Handler {
|
||||
h := &Handler{
|
||||
Router: mux.NewRouter(),
|
||||
helmPackageManager: helmPackageManager,
|
||||
|
@ -78,7 +79,7 @@ func NewTemplateHandler(bouncer security.BouncerService, helmPackageManager libh
|
|||
|
||||
// getHelmClusterAccess obtains the core k8s cluster access details from request.
|
||||
// The cluster access includes the cluster server url, the user's bearer token and the tls certificate.
|
||||
// The cluster access is passed in as kube config CLI params to helm binary.
|
||||
// The cluster access is passed in as kube config CLI params to helm.
|
||||
func (handler *Handler) getHelmClusterAccess(r *http.Request) (*options.KubernetesClusterAccess, *httperror.HandlerError) {
|
||||
endpoint, err := middlewares.FetchEndpoint(r)
|
||||
if err != nil {
|
||||
|
@ -107,6 +108,9 @@ func (handler *Handler) getHelmClusterAccess(r *http.Request) (*options.Kubernet
|
|||
|
||||
kubeConfigInternal := handler.kubeClusterAccessService.GetClusterDetails(hostURL, endpoint.ID, true)
|
||||
return &options.KubernetesClusterAccess{
|
||||
ClusterName: fmt.Sprintf("%s-%s", "portainer-cluster", endpoint.Name),
|
||||
ContextName: fmt.Sprintf("%s-%s", "portainer-ctx", endpoint.Name),
|
||||
UserName: fmt.Sprintf("%s-%s", "portainer-sa-user", tokenData.Username),
|
||||
ClusterServerURL: kubeConfigInternal.ClusterServerURL,
|
||||
CertificateAuthorityFile: kubeConfigInternal.CertificateAuthorityFile,
|
||||
AuthToken: bearerToken,
|
||||
|
|
|
@ -13,8 +13,8 @@ import (
|
|||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary/test"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ func Test_helmDelete(t *testing.T) {
|
|||
is.NoError(err, "Error initiating jwt service")
|
||||
|
||||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
|
|
|
@ -99,15 +99,11 @@ func (handler *Handler) installChart(r *http.Request, p installChartPayload) (*r
|
|||
}
|
||||
|
||||
installOpts := options.InstallOptions{
|
||||
Name: p.Name,
|
||||
Chart: p.Chart,
|
||||
Namespace: p.Namespace,
|
||||
Repo: p.Repo,
|
||||
KubernetesClusterAccess: &options.KubernetesClusterAccess{
|
||||
ClusterServerURL: clusterAccess.ClusterServerURL,
|
||||
CertificateAuthorityFile: clusterAccess.CertificateAuthorityFile,
|
||||
AuthToken: clusterAccess.AuthToken,
|
||||
},
|
||||
Name: p.Name,
|
||||
Chart: p.Chart,
|
||||
Namespace: p.Namespace,
|
||||
Repo: p.Repo,
|
||||
KubernetesClusterAccess: clusterAccess,
|
||||
}
|
||||
|
||||
if p.Values != "" {
|
||||
|
|
|
@ -15,9 +15,9 @@ import (
|
|||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary/test"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
|
||||
"github.com/segmentio/encoding/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -38,14 +38,14 @@ func Test_helmInstall(t *testing.T) {
|
|||
is.NoError(err, "Error initiating jwt service")
|
||||
|
||||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
// Install a single chart. We expect to get these values back
|
||||
options := options.InstallOptions{Name: "nginx-1", Chart: "nginx", Namespace: "default", Repo: "https://kubernetes.github.io/ingress-nginx"}
|
||||
options := options.InstallOptions{Name: "nginx-1", Chart: "nginx", Namespace: "default", Repo: "https://charts.bitnami.com/bitnami"}
|
||||
optdata, err := json.Marshal(options)
|
||||
is.NoError(err)
|
||||
|
||||
|
|
|
@ -14,9 +14,9 @@ import (
|
|||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/api/jwt"
|
||||
"github.com/portainer/portainer/api/kubernetes"
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary/test"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
|
||||
"github.com/segmentio/encoding/json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -37,7 +37,7 @@ func Test_helmList(t *testing.T) {
|
|||
is.NoError(err, "Error initialising jwt service")
|
||||
|
||||
kubernetesDeployer := exectest.NewKubernetesDeployer()
|
||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
kubeClusterAccessService := kubernetes.NewKubeClusterAccessService("", "", "")
|
||||
h := NewHandler(helper.NewTestRequestBouncer(), store, jwtService, kubernetesDeployer, helmPackageManager, kubeClusterAccessService)
|
||||
|
||||
|
|
|
@ -8,19 +8,19 @@ import (
|
|||
"testing"
|
||||
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary/test"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_helmRepoSearch(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
h := NewTemplateHandler(helper.NewTestRequestBouncer(), helmPackageManager)
|
||||
|
||||
assert.NotNil(t, h, "Handler should not fail")
|
||||
|
||||
repos := []string{"https://kubernetes.github.io/ingress-nginx", "https://portainer.github.io/k8s"}
|
||||
repos := []string{"https://charts.bitnami.com/bitnami", "https://portainer.github.io/k8s"}
|
||||
|
||||
for _, repo := range repos {
|
||||
t.Run(repo, func(t *testing.T) {
|
||||
|
|
|
@ -9,14 +9,14 @@ import (
|
|||
"testing"
|
||||
|
||||
helper "github.com/portainer/portainer/api/internal/testhelpers"
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary/test"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_helmShow(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
helmPackageManager := test.NewMockHelmBinaryPackageManager("")
|
||||
helmPackageManager := test.NewMockHelmPackageManager()
|
||||
h := NewTemplateHandler(helper.NewTestRequestBouncer(), helmPackageManager)
|
||||
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
@ -31,7 +31,7 @@ func Test_helmShow(t *testing.T) {
|
|||
t.Run(cmd, func(t *testing.T) {
|
||||
is.NotNil(h, "Handler should not fail")
|
||||
|
||||
repoUrlEncoded := url.QueryEscape("https://kubernetes.github.io/ingress-nginx")
|
||||
repoUrlEncoded := url.QueryEscape("https://charts.bitnami.com/bitnami")
|
||||
chart := "nginx"
|
||||
req := httptest.NewRequest("GET", fmt.Sprintf("/templates/helm/%s?repo=%s&chart=%s", cmd, repoUrlEncoded, chart), nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
|
|
@ -46,7 +46,7 @@ type settingsUpdatePayload struct {
|
|||
// Whether telemetry is enabled
|
||||
EnableTelemetry *bool `example:"false"`
|
||||
// Helm repository URL
|
||||
HelmRepositoryURL *string `example:"https://kubernetes.github.io/ingress-nginx"`
|
||||
HelmRepositoryURL *string `example:"https://charts.bitnami.com/bitnami"`
|
||||
// Kubectl Shell Image
|
||||
KubectlShellImage *string `example:"portainer/kubectl-shell:latest"`
|
||||
// TrustOnFirstConnect makes Portainer accepting edge agent connection by default
|
||||
|
|
|
@ -67,7 +67,7 @@ import (
|
|||
"github.com/portainer/portainer/api/platform"
|
||||
"github.com/portainer/portainer/api/scheduler"
|
||||
"github.com/portainer/portainer/api/stacks/deployments"
|
||||
"github.com/portainer/portainer/pkg/libhelm"
|
||||
libhelmtypes "github.com/portainer/portainer/pkg/libhelm/types"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
@ -103,7 +103,7 @@ type Server struct {
|
|||
DockerClientFactory *dockerclient.ClientFactory
|
||||
KubernetesClientFactory *cli.ClientFactory
|
||||
KubernetesDeployer portainer.KubernetesDeployer
|
||||
HelmPackageManager libhelm.HelmPackageManager
|
||||
HelmPackageManager libhelmtypes.HelmPackageManager
|
||||
Scheduler *scheduler.Scheduler
|
||||
ShutdownCtx context.Context
|
||||
ShutdownTrigger context.CancelFunc
|
||||
|
|
|
@ -588,7 +588,7 @@ type (
|
|||
// User identifier
|
||||
UserID UserID `json:"UserId" example:"1"`
|
||||
// Helm repository URL
|
||||
URL string `json:"URL" example:"https://kubernetes.github.io/ingress-nginx"`
|
||||
URL string `json:"URL" example:"https://charts.bitnami.com/bitnami"`
|
||||
}
|
||||
|
||||
// QuayRegistryData represents data required for Quay registry to work
|
||||
|
@ -984,8 +984,8 @@ type (
|
|||
KubeconfigExpiry string `json:"KubeconfigExpiry" example:"24h"`
|
||||
// Whether telemetry is enabled
|
||||
EnableTelemetry bool `json:"EnableTelemetry" example:"false"`
|
||||
// Helm repository URL, defaults to ""
|
||||
HelmRepositoryURL string `json:"HelmRepositoryURL"`
|
||||
// Helm repository URL, defaults to "https://charts.bitnami.com/bitnami"
|
||||
HelmRepositoryURL string `json:"HelmRepositoryURL" example:"https://charts.bitnami.com/bitnami"`
|
||||
// KubectlImage, defaults to portainer/kubectl-shell
|
||||
KubectlShellImage string `json:"KubectlShellImage" example:"portainer/kubectl-shell"`
|
||||
// TrustOnFirstConnect makes Portainer accepting edge agent connection by default
|
||||
|
@ -1673,8 +1673,8 @@ const (
|
|||
DefaultEdgeAgentCheckinIntervalInSeconds = 5
|
||||
// DefaultTemplatesURL represents the URL to the official templates supported by Portainer
|
||||
DefaultTemplatesURL = "https://raw.githubusercontent.com/portainer/templates/v3/templates.json"
|
||||
// DefaultHelmrepositoryURL set to empty string until oci support is added
|
||||
DefaultHelmRepositoryURL = ""
|
||||
// DefaultHelmrepositoryURL represents the URL to the official templates supported by Bitnami
|
||||
DefaultHelmRepositoryURL = "https://charts.bitnami.com/bitnami"
|
||||
// DefaultUserSessionTimeout represents the default timeout after which the user session is cleared
|
||||
DefaultUserSessionTimeout = "8h"
|
||||
// DefaultUserSessionTimeout represents the default timeout after which the user session is cleared
|
||||
|
|
|
@ -62,19 +62,19 @@ describe('HelmApplicationView', () => {
|
|||
expect(await screen.findByText('Helm details')).toBeInTheDocument();
|
||||
|
||||
// Check for the release details
|
||||
expect(screen.getByText('Release')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Release')).toBeInTheDocument();
|
||||
|
||||
// Check for the table content
|
||||
expect(screen.getByText('Name')).toBeInTheDocument();
|
||||
expect(screen.getByText('Chart')).toBeInTheDocument();
|
||||
expect(screen.getByText('App version')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Name')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Chart')).toBeInTheDocument();
|
||||
expect(await screen.findByText('App version')).toBeInTheDocument();
|
||||
|
||||
// Check for the actual values
|
||||
expect(screen.getByTestId('k8sAppDetail-appName')).toHaveTextContent(
|
||||
expect(await screen.findByTestId('k8sAppDetail-appName')).toHaveTextContent(
|
||||
'test-release'
|
||||
);
|
||||
expect(screen.getByText('test-chart-1.0.0')).toBeInTheDocument();
|
||||
expect(screen.getByText('1.0.0')).toBeInTheDocument();
|
||||
expect(await screen.findByText('test-chart-1.0.0')).toBeInTheDocument();
|
||||
expect(await screen.findByText('1.0.0')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display error message when API request fails', async () => {
|
||||
|
|
|
@ -1,37 +1,13 @@
|
|||
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||
|
||||
import { PageHeader } from '@/react/components/PageHeader';
|
||||
import { Widget, WidgetBody, WidgetTitle } from '@/react/components/Widget';
|
||||
import helm from '@/assets/ico/vendor/helm.svg?c';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { ViewLoading } from '@@/ViewLoading';
|
||||
import { Alert } from '@@/Alert';
|
||||
|
||||
import { useHelmRelease } from './queries/useHelmRelease';
|
||||
import { HelmDetailsWidget } from './HelmDetailsWidget';
|
||||
|
||||
export function HelmApplicationView() {
|
||||
const { params } = useCurrentStateAndParams();
|
||||
const environmentId = useEnvironmentId();
|
||||
|
||||
const name = params.name as string;
|
||||
const namespace = params.namespace as string;
|
||||
|
||||
const {
|
||||
data: release,
|
||||
isLoading,
|
||||
error,
|
||||
} = useHelmRelease(environmentId, name, namespace);
|
||||
|
||||
if (isLoading) {
|
||||
return <ViewLoading />;
|
||||
}
|
||||
|
||||
if (error || !release) {
|
||||
return (
|
||||
<Alert color="error" title="Failed to load Helm application details" />
|
||||
);
|
||||
}
|
||||
const { name, namespace } = params;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -46,32 +22,7 @@ export function HelmApplicationView() {
|
|||
|
||||
<div className="row">
|
||||
<div className="col-sm-12">
|
||||
<Widget>
|
||||
<WidgetTitle icon={helm} title="Release" />
|
||||
<WidgetBody>
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="!border-none w-40">Name</td>
|
||||
<td
|
||||
className="!border-none min-w-[140px]"
|
||||
data-cy="k8sAppDetail-appName"
|
||||
>
|
||||
{release.name}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="!border-t">Chart</td>
|
||||
<td className="!border-t">{release.chart}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>App version</td>
|
||||
<td>{release.app_version}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
<HelmDetailsWidget name={name} namespace={namespace} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
import {
|
||||
Loading,
|
||||
Widget,
|
||||
WidgetBody,
|
||||
WidgetTitle,
|
||||
} from '@/react/components/Widget';
|
||||
import helm from '@/assets/ico/vendor/helm.svg?c';
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { Alert } from '@@/Alert';
|
||||
|
||||
import { useHelmRelease } from './queries/useHelmRelease';
|
||||
|
||||
interface HelmDetailsWidgetProps {
|
||||
name: string;
|
||||
namespace: string;
|
||||
}
|
||||
|
||||
export function HelmDetailsWidget({ name, namespace }: HelmDetailsWidgetProps) {
|
||||
const environmentId = useEnvironmentId();
|
||||
|
||||
const {
|
||||
data: release,
|
||||
isInitialLoading,
|
||||
isError,
|
||||
} = useHelmRelease(environmentId, name, namespace);
|
||||
|
||||
return (
|
||||
<Widget>
|
||||
<WidgetTitle icon={helm} title="Release" />
|
||||
<WidgetBody>
|
||||
{isInitialLoading && <Loading />}
|
||||
|
||||
{isError && (
|
||||
<Alert
|
||||
color="error"
|
||||
title="Failed to load Helm application details"
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isInitialLoading && !isError && release && (
|
||||
<table className="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="!border-none w-40">Name</td>
|
||||
<td
|
||||
className="!border-none min-w-[140px]"
|
||||
data-cy="k8sAppDetail-appName"
|
||||
>
|
||||
{release.name}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="!border-t">Chart</td>
|
||||
<td className="!border-t">{release.chart}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>App version</td>
|
||||
<td>{release.app_version}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
)}
|
||||
</WidgetBody>
|
||||
</Widget>
|
||||
);
|
||||
}
|
|
@ -23,7 +23,6 @@ GIT_COMMIT_HASH=${GIT_COMMIT_HASH:-$(git rev-parse --short HEAD)}
|
|||
|
||||
# populate dependencies versions
|
||||
DOCKER_VERSION=$(jq -r '.docker' < "${BINARY_VERSION_FILE}")
|
||||
HELM_VERSION=$(jq -r '.helm' < "${BINARY_VERSION_FILE}")
|
||||
KUBECTL_VERSION=$(jq -r '.kubectl' < "${BINARY_VERSION_FILE}")
|
||||
COMPOSE_VERSION=$(go list -m -f '{{.Version}}' github.com/docker/compose/v2)
|
||||
|
||||
|
@ -52,7 +51,6 @@ ldflags="-s -X 'github.com/portainer/liblicense.LicenseServerBaseURL=https://api
|
|||
-X 'github.com/portainer/portainer/pkg/build.GoVersion=${GO_VERSION}' \
|
||||
-X 'github.com/portainer/portainer/pkg/build.DepComposeVersion=${COMPOSE_VERSION}' \
|
||||
-X 'github.com/portainer/portainer/pkg/build.DepDockerVersion=${DOCKER_VERSION}' \
|
||||
-X 'github.com/portainer/portainer/pkg/build.DepHelmVersion=${HELM_VERSION}' \
|
||||
-X 'github.com/portainer/portainer/pkg/build.DepKubectlVersion=${KUBECTL_VERSION}'"
|
||||
|
||||
echo "$ldflags"
|
||||
|
|
|
@ -17,12 +17,10 @@ echo "Checking and downloading binaries for docker ${dockerVersion}, helm ${helm
|
|||
|
||||
# Determine the binary file names based on the platform
|
||||
dockerBinary="dist/docker"
|
||||
helmBinary="dist/helm"
|
||||
kubectlBinary="dist/kubectl"
|
||||
|
||||
if [ "$PLATFORM" == "windows" ]; then
|
||||
dockerBinary="dist/docker.exe"
|
||||
helmBinary="dist/helm.exe"
|
||||
kubectlBinary="dist/kubectl.exe"
|
||||
fi
|
||||
|
||||
|
@ -34,14 +32,6 @@ else
|
|||
echo "Docker binary already exists, skipping download."
|
||||
fi
|
||||
|
||||
# Check and download helm binary
|
||||
if [ ! -f "$helmBinary" ]; then
|
||||
echo "Downloading helm binary..."
|
||||
/usr/bin/env bash ./build/download_helm_binary.sh "$PLATFORM" "$ARCH" "$helmVersion"
|
||||
else
|
||||
echo "Helm binary already exists, skipping download."
|
||||
fi
|
||||
|
||||
# Check and download kubectl binary
|
||||
if [ ! -f "$kubectlBinary" ]; then
|
||||
echo "Downloading kubectl binary..."
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -ne 3 ]]; then
|
||||
echo "Illegal number of parameters" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PLATFORM=$1
|
||||
ARCH=$2
|
||||
HELM_VERSION=$3
|
||||
HELM_DIST="helm-$HELM_VERSION-$PLATFORM-$ARCH"
|
||||
|
||||
|
||||
if [[ ${PLATFORM} == "windows" ]]; then
|
||||
wget --tries=3 --waitretry=30 --quiet -O tmp.zip "https://get.helm.sh/${HELM_DIST}.zip" && unzip -o -j tmp.zip "${PLATFORM}-${ARCH}/helm.exe" -d dist && rm -f tmp.zip
|
||||
else
|
||||
wget -qO- "https://get.helm.sh/${HELM_DIST}.tar.gz" | tar -x -z --strip-components 1 "${PLATFORM}-${ARCH}/helm"
|
||||
mv "helm" "dist/helm"
|
||||
chmod +x "dist/helm"
|
||||
fi
|
|
@ -11,7 +11,6 @@ LABEL org.opencontainers.image.title="Portainer" \
|
|||
com.docker.extension.additional-urls="[{\"title\":\"Website\",\"url\":\"https://www.portainer.io?utm_campaign=DockerCon&utm_source=DockerDesktop\"},{\"title\":\"Documentation\",\"url\":\"https://docs.portainer.io\"},{\"title\":\"Support\",\"url\":\"https://join.slack.com/t/portainer/shared_invite/zt-txh3ljab-52QHTyjCqbe5RibC2lcjKA\"}]"
|
||||
|
||||
COPY dist/docker /
|
||||
COPY dist/helm /
|
||||
COPY dist/kubectl /
|
||||
COPY dist/mustache-templates /mustache-templates/
|
||||
COPY dist/portainer /
|
||||
|
|
|
@ -11,7 +11,6 @@ LABEL org.opencontainers.image.title="Portainer" \
|
|||
com.docker.extension.additional-urls="[{\"title\":\"Website\",\"url\":\"https://www.portainer.io?utm_campaign=DockerCon&utm_source=DockerDesktop\"},{\"title\":\"Documentation\",\"url\":\"https://docs.portainer.io\"},{\"title\":\"Support\",\"url\":\"https://join.slack.com/t/portainer/shared_invite/zt-txh3ljab-52QHTyjCqbe5RibC2lcjKA\"}]"
|
||||
|
||||
COPY dist/docker /
|
||||
COPY dist/helm /
|
||||
COPY dist/kubectl /
|
||||
COPY dist/mustache-templates /mustache-templates/
|
||||
COPY dist/portainer /
|
||||
|
|
|
@ -10,7 +10,6 @@ USER ContainerAdministrator
|
|||
|
||||
COPY dist/mingit/ mingit/
|
||||
COPY dist/docker.exe /
|
||||
COPY dist/helm.exe /
|
||||
COPY dist/kubectl.exe /
|
||||
COPY dist/mustache-templates /mustache-templates/
|
||||
COPY dist/portainer.exe /
|
||||
|
|
114
go.mod
114
go.mod
|
@ -55,10 +55,11 @@ require (
|
|||
golang.org/x/sync v0.10.0
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.29.2
|
||||
k8s.io/apimachinery v0.29.2
|
||||
k8s.io/client-go v0.29.2
|
||||
k8s.io/metrics v0.27.4
|
||||
helm.sh/helm/v3 v3.17.1
|
||||
k8s.io/api v0.32.1
|
||||
k8s.io/apimachinery v0.32.1
|
||||
k8s.io/client-go v0.32.1
|
||||
k8s.io/metrics v0.32.1
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78
|
||||
)
|
||||
|
||||
|
@ -70,8 +71,12 @@ require (
|
|||
github.com/AlecAivazis/survey/v2 v2.3.7 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/squirrel v1.5.4 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.3 // indirect
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
|
@ -89,9 +94,11 @@ require (
|
|||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.4 // indirect
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/containerd/console v1.0.4 // indirect
|
||||
|
@ -106,8 +113,8 @@ require (
|
|||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/containers/ocicrypt v1.1.10 // indirect
|
||||
github.com/containers/storage v1.53.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/buildx v0.18.0 // indirect
|
||||
github.com/docker/cli-docs-tool v0.8.0 // indirect
|
||||
|
@ -120,39 +127,49 @@ require (
|
|||
github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsevents v0.2.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.10 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/gnostic-models v0.6.8 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 // indirect
|
||||
github.com/gosuri/uitable v0.0.4 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/in-toto/in-toto-golang v0.9.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmoiron/sqlx v1.4.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/jpillora/ansi v1.0.3 // indirect
|
||||
|
@ -162,22 +179,28 @@ require (
|
|||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
|
||||
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
|
||||
github.com/leodido/go-urn v1.2.2 // 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.15 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.12 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/buildkit v0.17.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/spdystream v0.2.0 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
github.com/moby/sys/capability v0.4.0 // indirect
|
||||
github.com/moby/sys/mountinfo v0.7.2 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
|
@ -188,28 +211,34 @@ require (
|
|||
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/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.2.0 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.17.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.44.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/r3labs/sse v0.0.0-20210224172625-26fe804710bc // indirect
|
||||
github.com/rivo/uniseg v0.4.4 // indirect
|
||||
github.com/rubenv/sql-migrate v1.7.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
|
||||
github.com/segmentio/asm v1.1.3 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 // indirect
|
||||
|
@ -223,44 +252,55 @@ require (
|
|||
github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/vbatts/tar-split v0.11.5 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect
|
||||
go.opentelemetry.io/otel v1.25.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.25.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.25.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
|
||||
google.golang.org/grpc v1.68.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.110.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.32.1 // indirect
|
||||
k8s.io/apiserver v0.32.1 // indirect
|
||||
k8s.io/cli-runtime v0.32.1 // indirect
|
||||
k8s.io/component-base v0.32.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
||||
k8s.io/kubectl v0.32.1 // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
oras.land/oras-go v1.2.5 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.18.0 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
tags.cncf.io/container-device-interface v0.8.0 // indirect
|
||||
)
|
||||
|
|
297
go.sum
297
go.sum
|
@ -1,9 +1,7 @@
|
|||
cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM=
|
||||
cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg=
|
||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
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/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA=
|
||||
|
@ -15,12 +13,22 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
|
|||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
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/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
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 v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/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.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
|
@ -90,7 +98,11 @@ 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/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw=
|
||||
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
|
||||
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/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY=
|
||||
github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE=
|
||||
github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc=
|
||||
|
@ -106,13 +118,13 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
|
|||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/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/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8=
|
||||
github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
|
||||
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
|
||||
github.com/compose-spec/compose-go/v2 v2.4.5 h1:p4ih4Jb6VgGPLPxh3fSFVKAjFHtZd+7HVLCSFzcFx9Y=
|
||||
|
@ -161,14 +173,17 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
|
|||
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
|
||||
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
|
||||
github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5 h1:RAV05c0xOkJ3dZGS0JFybxFKZ2WMLabgx3uXnd7rpGs=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2 h1:aBfCb7iqHmDEIp6fBvC/hQUddQfg+3qdYjwzaiP9Hnc=
|
||||
github.com/distribution/distribution/v3 v3.0.0-20221208165359-362910506bc2/go.mod h1:WHNsWjnIn2V1LYOrME7e8KxSeKunYHsxEm4am0BUtcI=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/docker/buildx v0.18.0 h1:rSauXHeJt90NvtXrLK5J992Eb0UPJZs2vV3u1zTf1nE=
|
||||
|
@ -209,13 +224,19 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER
|
|||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4=
|
||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84=
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7DlmewI=
|
||||
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c=
|
||||
github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQwKBr1qH/w=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
|
@ -223,12 +244,16 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
|||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw=
|
||||
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/g07cha/defender v0.0.0-20180505193036-5665c627c814 h1:gWvniJ4GbFfkf700kykAImbLiEMU0Q3QN9hQ26Js1pU=
|
||||
github.com/g07cha/defender v0.0.0-20180505193036-5665c627c814/go.mod h1:secRm32Ro77eD23BmPVbgLbWN+JWDw7pJszenjxI4bI=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
|
||||
|
@ -237,24 +262,26 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
|
|||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.13.0 h1:vLn5wlGIh/X78El6r3Jr+30W16Blk0CTcxTYcYPWi5E=
|
||||
github.com/go-git/go-git/v5 v5.13.0/go.mod h1:Wjo7/JyVKtQgUNdXYXIepzWfJQkUEIGvkvVkiXRR/zw=
|
||||
github.com/go-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-ldap/ldap/v3 v3.4.1 h1:fU/0xli6HY02ocbMuozHAYsaHLcnkLjvho2r5a34BUU=
|
||||
github.com/go-ldap/ldap/v3 v3.4.1/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
|
||||
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/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.22.10 h1:4y86NVn7Z2yYd6pfS4Z+Nyh3aAUL3Nul+LMbhFKy0gA=
|
||||
github.com/go-openapi/swag v0.22.10/go.mod h1:Cnn8BYtRlx6BNE3DPN86f/xkapGIcLWzh3CLEb4C1jI=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
|
@ -264,13 +291,15 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91
|
|||
github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI=
|
||||
github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
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-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
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/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
|
@ -293,6 +322,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
|||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
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/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
|
||||
github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ=
|
||||
|
@ -305,24 +338,29 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
|
|||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg=
|
||||
github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
|
||||
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
|
||||
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.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||
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/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
|
||||
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-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
|
@ -339,8 +377,8 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
|
|||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
||||
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
|
@ -357,8 +395,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
|
|||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/jmoiron/sqlx v1.3.3 h1:j82X0bf7oQ27XeqxicSZsTU5suPwKElg3oyxNn43iTk=
|
||||
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
|
||||
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
|
||||
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
|
||||
github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
|
||||
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4=
|
||||
|
@ -402,9 +440,17 @@ 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/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4=
|
||||
github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ=
|
||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/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/magiconair/properties v1.5.3 h1:C8fxWnhYyME3n0klPOhVM7PtYUB3eV1W3DeFmN3j53Y=
|
||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
|
@ -423,19 +469,27 @@ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
|
|||
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
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/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU=
|
||||
github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
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/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/moby/buildkit v0.17.2 h1:/jgk/MuXbA7jeXMkknOpHYB+Ct4aNvQHkBB7SxD3D4U=
|
||||
github.com/moby/buildkit v0.17.2/go.mod h1:vr5vltV8wt4F2jThbNOChfbAklJ0DOW11w36v210hOg=
|
||||
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
|
||||
|
@ -444,8 +498,8 @@ github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
|||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
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/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU=
|
||||
github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
|
||||
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
|
@ -469,6 +523,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||
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=
|
||||
|
@ -480,12 +536,12 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
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/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
|
@ -505,6 +561,10 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR
|
|||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI=
|
||||
github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -513,24 +573,27 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
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.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
|
||||
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
|
||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
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.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
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/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
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=
|
||||
|
@ -544,11 +607,14 @@ github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
|||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w=
|
||||
github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
|
||||
github.com/rubenv/sql-migrate v1.7.1 h1:f/o0WgfO/GqNuVg+6801K/KW3WdDSupzSjDYODmiUq4=
|
||||
github.com/rubenv/sql-migrate v1.7.1/go.mod h1:Ob2Psprc0/3ggbM6wCzyYVFFuc6FyZrb2AS+ezLDFb4=
|
||||
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/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA=
|
||||
|
@ -563,6 +629,8 @@ github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQ
|
|||
github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
|
@ -575,8 +643,9 @@ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EE
|
|||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQraY=
|
||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94 h1:JmfC365KywYwHB946TTiQWEb8kqPY+pybPLoGE9GgVk=
|
||||
github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
|
@ -592,6 +661,8 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
|
@ -632,6 +703,8 @@ github.com/viney-shih/go-lock v1.1.1 h1:SwzDPPAiHpcwGCr5k8xD15d2gQSo8d4roRYd7TDV
|
|||
github.com/viney-shih/go-lock v1.1.1/go.mod h1:Yijm78Ljteb3kRiJrbLAxVntkUukGu5uzSxq/xV7OO8=
|
||||
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b h1:FsyNrX12e5BkplJq7wKOLk0+C6LZ+KGXvuEcKUYm5ss=
|
||||
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
|
@ -641,9 +714,17 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo
|
|||
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=
|
||||
github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc h1:zkGwegkOW709y0oiAraH/3D8njopUR/pARHv4tZZ6pw=
|
||||
github.com/zmap/zcrypto v0.0.0-20210511125630-18f1e0152cfc/go.mod h1:FM4U1E3NzlNMRnSUTU3P1UdukWhYGifqEsjk9fn7BCk=
|
||||
github.com/zmap/zlint/v3 v3.1.0 h1:WjVytZo79m/L1+/Mlphl09WBob6YTGljN5IGWZFpAv0=
|
||||
|
@ -652,34 +733,34 @@ go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
|||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
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/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1 h1:gbhw/u49SS3gkPWiYweQNJGm/uJN5GkI/FrosxSHT7A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.46.1/go.mod h1:GnOaBaFQ2we3b9AGWJpsBa7v1S5RlQzlC3O7dRMxZhM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8=
|
||||
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
|
||||
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0 h1:tIqheXEFWAZ7O8A7m+J0aPTmpJN3YQ7qetUAdkkkKpk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.21.0/go.mod h1:nUeKExfxAQVbiVFn32YXpXZZHZ61Cc3s3Rn1pDBGAb0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8=
|
||||
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
|
||||
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
|
||||
go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
|
||||
go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q=
|
||||
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
|
||||
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
|
||||
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||
|
@ -769,15 +850,15 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -805,6 +886,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
|
@ -826,26 +909,44 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A=
|
||||
k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0=
|
||||
k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8=
|
||||
k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU=
|
||||
k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg=
|
||||
k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA=
|
||||
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/metrics v0.27.4 h1:2s04bods7rA507iouGbxD55YrKNlFjLYzm30noOl9Sk=
|
||||
k8s.io/metrics v0.27.4/go.mod h1:kRvfhFC7wCQEFvu6H92uiV7v05z3Ty/vtluYT5D2Xpk=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
|
||||
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
helm.sh/helm/v3 v3.17.1 h1:gzVoAD+qVuoJU6KDMSAeo0xRJ6N1znRxz3wyuXRmJDk=
|
||||
helm.sh/helm/v3 v3.17.1/go.mod h1:nvreuhuR+j78NkQcLC3TYoprCKStLyw5P4T7E5itv2w=
|
||||
k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
|
||||
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||
k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
|
||||
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
|
||||
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak=
|
||||
k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw=
|
||||
k8s.io/cli-runtime v0.32.1 h1:19nwZPlYGJPUDbhAxDIS2/oydCikvKMHsxroKNGA2mM=
|
||||
k8s.io/cli-runtime v0.32.1/go.mod h1:NJPbeadVFnV2E7B7vF+FvU09mpwYlZCu8PqjzfuOnkY=
|
||||
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
|
||||
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||
k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk=
|
||||
k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y=
|
||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4=
|
||||
k8s.io/kubectl v0.32.1 h1:/btLtXLQUU1rWx8AEvX9jrb9LaI6yeezt3sFALhB8M8=
|
||||
k8s.io/kubectl v0.32.1/go.mod h1:sezNuyWi1STk4ZNPVRIFfgjqMI6XMf+oCVLjZen/pFQ=
|
||||
k8s.io/metrics v0.32.1 h1:Ou4nrEtZS2vFf7OJCf9z3+2kr0A00kQzfoSwxg0gXps=
|
||||
k8s.io/metrics v0.32.1/go.mod h1:cLnai9XKYby1tNMX+xe8p9VLzTqrxYPcmqfCBoWObcM=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo=
|
||||
oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo=
|
||||
sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U=
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E=
|
||||
sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78 h1:SqYE5+A2qvRhErbsXFfUEUmpWEKxxRSMgGLkvRAFOV4=
|
||||
software.sslmate.com/src/go-pkcs12 v0.0.0-20210415151418-c5206de65a78/go.mod h1:B7Wf0Ya4DHF9Yw+qfZuJijQYkWicqDa+79Ytmmq3Kjg=
|
||||
tags.cncf.io/container-device-interface v0.8.0 h1:8bCFo/g9WODjWx3m6EYl3GfUG31eKJbaggyBDxEldRc=
|
||||
|
|
|
@ -42,9 +42,6 @@ var (
|
|||
// DepDockerVersion is the version of the Docker binary shipped with the application.
|
||||
DepDockerVersion string
|
||||
|
||||
// DepHelmVersion is the version of the Helm binary shipped with the application.
|
||||
DepHelmVersion string
|
||||
|
||||
// DepKubectlVersion is the version of the Kubectl binary shipped with the application.
|
||||
DepKubectlVersion string
|
||||
)
|
||||
|
@ -92,7 +89,6 @@ func GetBuildInfo() BuildInfo {
|
|||
func GetDependenciesInfo() DependenciesInfo {
|
||||
return DependenciesInfo{
|
||||
DockerVersion: DepDockerVersion,
|
||||
HelmVersion: DepHelmVersion,
|
||||
KubectlVersion: DepKubectlVersion,
|
||||
ComposeVersion: DepComposeVersion,
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
)
|
||||
|
||||
// Get runs `helm get` with specified get options.
|
||||
// The get options translate to CLI arguments which are passed in to the helm binary when executing install.
|
||||
func (hbpm *helmBinaryPackageManager) Get(getOpts options.GetOptions) ([]byte, error) {
|
||||
if getOpts.Name == "" || getOpts.ReleaseResource == "" {
|
||||
return nil, errors.New("release name and release resource are required")
|
||||
}
|
||||
|
||||
args := []string{
|
||||
string(getOpts.ReleaseResource),
|
||||
getOpts.Name,
|
||||
}
|
||||
if getOpts.Namespace != "" {
|
||||
args = append(args, "--namespace", getOpts.Namespace)
|
||||
}
|
||||
|
||||
result, err := hbpm.runWithKubeConfig("get", args, getOpts.KubernetesClusterAccess, getOpts.Env)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run helm get on specified args")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
)
|
||||
|
||||
// helmBinaryPackageManager is a wrapper for the helm binary which implements HelmPackageManager
|
||||
type helmBinaryPackageManager struct {
|
||||
binaryPath string
|
||||
}
|
||||
|
||||
// NewHelmBinaryPackageManager initializes a new HelmPackageManager service.
|
||||
func NewHelmBinaryPackageManager(binaryPath string) *helmBinaryPackageManager {
|
||||
return &helmBinaryPackageManager{binaryPath: binaryPath}
|
||||
}
|
||||
|
||||
// runWithKubeConfig will execute run against the provided Kubernetes cluster with kubeconfig as cli arguments.
|
||||
func (hbpm *helmBinaryPackageManager) runWithKubeConfig(command string, args []string, kca *options.KubernetesClusterAccess, env []string) ([]byte, error) {
|
||||
cmdArgs := make([]string, 0)
|
||||
if kca != nil {
|
||||
cmdArgs = append(cmdArgs, "--kube-apiserver", kca.ClusterServerURL)
|
||||
cmdArgs = append(cmdArgs, "--kube-token", kca.AuthToken)
|
||||
cmdArgs = append(cmdArgs, "--kube-ca-file", kca.CertificateAuthorityFile)
|
||||
}
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
return hbpm.run(command, cmdArgs, env)
|
||||
}
|
||||
|
||||
// run will execute helm command against the provided Kubernetes cluster.
|
||||
// The endpointId and authToken are dynamic params (based on the user) that allow helm to execute commands
|
||||
// in the context of the current user against specified k8s cluster.
|
||||
func (hbpm *helmBinaryPackageManager) run(command string, args []string, env []string) ([]byte, error) {
|
||||
cmdArgs := make([]string, 0)
|
||||
cmdArgs = append(cmdArgs, command)
|
||||
cmdArgs = append(cmdArgs, args...)
|
||||
|
||||
helmPath := path.Join(hbpm.binaryPath, "helm")
|
||||
if runtime.GOOS == "windows" {
|
||||
helmPath = path.Join(hbpm.binaryPath, "helm.exe")
|
||||
}
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd := exec.Command(helmPath, cmdArgs...)
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
cmd.Env = os.Environ()
|
||||
cmd.Env = append(cmd.Env, env...)
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, stderr.String())
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/encoding/json"
|
||||
)
|
||||
|
||||
// Install runs `helm install` with specified install options.
|
||||
// The install options translate to CLI arguments which are passed in to the helm binary when executing install.
|
||||
func (hbpm *helmBinaryPackageManager) Install(installOpts options.InstallOptions) (*release.Release, error) {
|
||||
if installOpts.Name == "" {
|
||||
installOpts.Name = "--generate-name"
|
||||
}
|
||||
args := []string{
|
||||
installOpts.Name,
|
||||
installOpts.Chart,
|
||||
"--repo", installOpts.Repo,
|
||||
"--output", "json",
|
||||
}
|
||||
if installOpts.Namespace != "" {
|
||||
args = append(args, "--namespace", installOpts.Namespace)
|
||||
}
|
||||
if installOpts.ValuesFile != "" {
|
||||
args = append(args, "--values", installOpts.ValuesFile)
|
||||
}
|
||||
if installOpts.Wait {
|
||||
args = append(args, "--wait")
|
||||
}
|
||||
if installOpts.PostRenderer != "" {
|
||||
args = append(args, "--post-renderer", installOpts.PostRenderer)
|
||||
}
|
||||
|
||||
result, err := hbpm.runWithKubeConfig("install", args, installOpts.KubernetesClusterAccess, installOpts.Env)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run helm install on specified args")
|
||||
}
|
||||
|
||||
response := &release.Release{}
|
||||
err = json.Unmarshal(result, &response)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to unmarshal helm install response to Release struct")
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func createValuesFile(values string) (string, error) {
|
||||
file, err := os.CreateTemp("", "helm-values")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = file.WriteString(values)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return file.Name(), nil
|
||||
}
|
||||
|
||||
// getHelmBinaryPath is helper function to get local helm binary path (if helm is in path)
|
||||
func getHelmBinaryPath() (string, error) {
|
||||
path, err := exec.LookPath("helm")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dir, err := filepath.Abs(filepath.Dir(path))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
func Test_Install(t *testing.T) {
|
||||
ensureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
|
||||
path, err := getHelmBinaryPath()
|
||||
is.NoError(err, "helm binary must exist in path to run tests")
|
||||
|
||||
hbpm := NewHelmBinaryPackageManager(path)
|
||||
|
||||
t.Run("successfully installs nginx chart with name test-nginx", func(t *testing.T) {
|
||||
// helm install test-nginx --repo https://kubernetes.github.io/ingress-nginx nginx
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx",
|
||||
Chart: "nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{"test-nginx"}, nil)
|
||||
|
||||
is.NoError(err, "should successfully install release", release)
|
||||
})
|
||||
|
||||
t.Run("successfully installs nginx chart with generated name", func(t *testing.T) {
|
||||
// helm install --generate-name --repo https://kubernetes.github.io/ingress-nginx nginx
|
||||
installOpts := options.InstallOptions{
|
||||
Chart: "nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{release.Name}, nil)
|
||||
|
||||
is.NoError(err, "should successfully install release", release)
|
||||
})
|
||||
|
||||
t.Run("successfully installs nginx with values", func(t *testing.T) {
|
||||
// helm install test-nginx-2 --repo https://kubernetes.github.io/ingress-nginx nginx --values /tmp/helm-values3161785816
|
||||
values, err := createValuesFile("service:\n port: 8081")
|
||||
is.NoError(err, "should create a values file")
|
||||
|
||||
defer os.Remove(values)
|
||||
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx-2",
|
||||
Chart: "nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
ValuesFile: values,
|
||||
}
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{"test-nginx-2"}, nil)
|
||||
|
||||
is.NoError(err, "should successfully install release", release)
|
||||
})
|
||||
|
||||
t.Run("successfully installs portainer chart with name portainer-test", func(t *testing.T) {
|
||||
// helm install portainer-test portainer --repo https://portainer.github.io/k8s/
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "portainer-test",
|
||||
Chart: "portainer",
|
||||
Repo: "https://portainer.github.io/k8s/",
|
||||
}
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{installOpts.Name}, nil)
|
||||
|
||||
is.NoError(err, "should successfully install release", release)
|
||||
})
|
||||
}
|
||||
|
||||
func ensureIntegrationTest(t *testing.T) {
|
||||
if _, ok := os.LookupEnv("INTEGRATION_TEST"); !ok {
|
||||
t.Skip("skip an integration test")
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/encoding/json"
|
||||
)
|
||||
|
||||
// List runs `helm list --output json --filter <filter> --selector <selector> --namespace <namespace>` with specified list options.
|
||||
// The list options translate to CLI args the helm binary
|
||||
func (hbpm *helmBinaryPackageManager) List(listOpts options.ListOptions) ([]release.ReleaseElement, error) {
|
||||
args := []string{"--output", "json"}
|
||||
|
||||
if listOpts.Filter != "" {
|
||||
args = append(args, "--filter", listOpts.Filter)
|
||||
}
|
||||
if listOpts.Selector != "" {
|
||||
args = append(args, "--selector", listOpts.Selector)
|
||||
}
|
||||
if listOpts.Namespace != "" {
|
||||
args = append(args, "--namespace", listOpts.Namespace)
|
||||
}
|
||||
|
||||
result, err := hbpm.runWithKubeConfig("list", args, listOpts.KubernetesClusterAccess, listOpts.Env)
|
||||
if err != nil {
|
||||
return []release.ReleaseElement{}, errors.Wrap(err, "failed to run helm list on specified args")
|
||||
}
|
||||
|
||||
response := []release.ReleaseElement{}
|
||||
err = json.Unmarshal(result, &response)
|
||||
if err != nil {
|
||||
return []release.ReleaseElement{}, errors.Wrap(err, "failed to unmarshal helm list response to releastElement list")
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package binary
|
||||
|
||||
// Package common implements common functionality for the helm.
|
||||
// The functionality does not rely on the implementation of `HelmPackageManager`
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/encoding/json"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var errRequiredSearchOptions = errors.New("repo is required")
|
||||
var errInvalidRepoURL = errors.New("the request failed since either the Helm repository was not found or the index.yaml is not valid")
|
||||
|
||||
type File struct {
|
||||
APIVersion string `yaml:"apiVersion" json:"apiVersion"`
|
||||
Entries map[string][]Entry `yaml:"entries" json:"entries"`
|
||||
Generated string `yaml:"generated" json:"generated"`
|
||||
}
|
||||
|
||||
type Annotations struct {
|
||||
Category string `yaml:"category" json:"category"`
|
||||
}
|
||||
|
||||
type Entry struct {
|
||||
Annotations *Annotations `yaml:"annotations" json:"annotations,omitempty"`
|
||||
Created string `yaml:"created" json:"created"`
|
||||
Deprecated bool `yaml:"deprecated" json:"deprecated"`
|
||||
Description string `yaml:"description" json:"description"`
|
||||
Digest string `yaml:"digest" json:"digest"`
|
||||
Home string `yaml:"home" json:"home"`
|
||||
Name string `yaml:"name" json:"name"`
|
||||
Sources []string `yaml:"sources" json:"sources"`
|
||||
Urls []string `yaml:"urls" json:"urls"`
|
||||
Version string `yaml:"version" json:"version"`
|
||||
Icon string `yaml:"icon" json:"icon,omitempty"`
|
||||
}
|
||||
|
||||
// SearchRepo downloads the `index.yaml` file for specified repo, parses it and returns JSON to caller.
|
||||
// The functionality is similar to that of what `helm search repo [chart] --repo <repo>` CLI runs;
|
||||
// this approach is used instead since the `helm search repo` requires a repo to be added to the global helm cache
|
||||
func (hbpm *helmBinaryPackageManager) SearchRepo(searchRepoOpts options.SearchRepoOptions) ([]byte, error) {
|
||||
if searchRepoOpts.Repo == "" {
|
||||
return nil, errRequiredSearchOptions
|
||||
}
|
||||
|
||||
client := searchRepoOpts.Client
|
||||
|
||||
if client == nil {
|
||||
// The current index.yaml is ~9MB on bitnami.
|
||||
// At a slow @2mbit download = 40s. @100bit = ~1s.
|
||||
// I'm seeing 3 - 4s over wifi.
|
||||
// Give ample time but timeout for now. Can be improved in the future
|
||||
client = &http.Client{
|
||||
Timeout: 300 * time.Second,
|
||||
Transport: http.DefaultTransport,
|
||||
}
|
||||
}
|
||||
|
||||
// Allow redirect behavior to be overridden if specified.
|
||||
if client.CheckRedirect == nil {
|
||||
client.CheckRedirect = defaultCheckRedirect
|
||||
}
|
||||
|
||||
url, err := url.ParseRequestURI(searchRepoOpts.Repo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid helm chart URL: "+searchRepoOpts.Repo)
|
||||
}
|
||||
|
||||
url.Path = path.Join(url.Path, "index.yaml")
|
||||
resp, err := client.Get(url.String())
|
||||
if err != nil {
|
||||
return nil, errInvalidRepoURL
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var file File
|
||||
err = yaml.NewDecoder(resp.Body).Decode(&file)
|
||||
if err != nil {
|
||||
return nil, errInvalidRepoURL
|
||||
}
|
||||
|
||||
// Validate index.yaml
|
||||
if file.APIVersion == "" || file.Entries == nil {
|
||||
return nil, errInvalidRepoURL
|
||||
}
|
||||
|
||||
result, err := json.Marshal(file)
|
||||
if err != nil {
|
||||
return nil, errInvalidRepoURL
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// defaultCheckRedirect is a default CheckRedirect for helm
|
||||
// We don't allow redirects to URLs not ending in index.yaml
|
||||
// After that we follow the go http client behavior which is to stop
|
||||
// after a maximum of 10 redirects
|
||||
func defaultCheckRedirect(req *http.Request, via []*http.Request) error {
|
||||
// The request url must end in index.yaml
|
||||
if path.Base(req.URL.Path) != "index.yaml" {
|
||||
return errors.New("the request URL must end in index.yaml")
|
||||
}
|
||||
|
||||
// default behavior below
|
||||
if len(via) >= 10 {
|
||||
return errors.New("stopped after 10 redirects")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/libhelmtest"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_SearchRepo(t *testing.T) {
|
||||
libhelmtest.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
|
||||
hpm := NewHelmBinaryPackageManager("")
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
url string
|
||||
invalid bool
|
||||
}
|
||||
|
||||
tests := []testCase{
|
||||
{"not a helm repo", "https://portainer.io", true},
|
||||
{"ingress helm repo", "https://kubernetes.github.io/ingress-nginx", false},
|
||||
{"portainer helm repo", "https://portainer.github.io/k8s/", false},
|
||||
{"gitlap helm repo with trailing slash", "https://charts.gitlab.io/", false},
|
||||
{"elastic helm repo with trailing slash", "https://helm.elastic.co/", false},
|
||||
{"fabric8.io helm repo with trailing slash", "https://fabric8.io/helm/", false},
|
||||
{"lensesio helm repo without trailing slash", "https://lensesio.github.io/kafka-helm-charts", false},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
func(tc testCase) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
response, err := hpm.SearchRepo(options.SearchRepoOptions{Repo: tc.url})
|
||||
if tc.invalid {
|
||||
is.Errorf(err, "error expected: %s", tc.url)
|
||||
} else {
|
||||
is.NoError(err, "no error expected: %s", tc.url)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
is.NotEmpty(response, "response expected")
|
||||
}
|
||||
})
|
||||
}(test)
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
)
|
||||
|
||||
var errRequiredShowOptions = errors.New("chart, repo and output format are required")
|
||||
|
||||
// Show runs `helm show <command> <chart> --repo <repo>` with specified show options.
|
||||
// The show options translate to CLI arguments which are passed in to the helm binary when executing install.
|
||||
func (hbpm *helmBinaryPackageManager) Show(showOpts options.ShowOptions) ([]byte, error) {
|
||||
if showOpts.Chart == "" || showOpts.Repo == "" || showOpts.OutputFormat == "" {
|
||||
return nil, errRequiredShowOptions
|
||||
}
|
||||
|
||||
args := []string{
|
||||
string(showOpts.OutputFormat),
|
||||
showOpts.Chart,
|
||||
"--repo", showOpts.Repo,
|
||||
}
|
||||
|
||||
result, err := hbpm.run("show", args, showOpts.Env)
|
||||
if err != nil {
|
||||
return nil, errors.New("the request failed since either the Helm repository was not found or the chart does not exist")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
)
|
||||
|
||||
var errRequiredUninstallOptions = errors.New("release name is required")
|
||||
|
||||
// Uninstall runs `helm uninstall <name> --namespace <namespace>` with specified uninstall options.
|
||||
// The uninstall options translate to CLI arguments which are passed in to the helm binary when executing uninstall.
|
||||
func (hbpm *helmBinaryPackageManager) Uninstall(uninstallOpts options.UninstallOptions) error {
|
||||
if uninstallOpts.Name == "" {
|
||||
return errRequiredUninstallOptions
|
||||
}
|
||||
|
||||
args := []string{uninstallOpts.Name}
|
||||
|
||||
if uninstallOpts.Namespace != "" {
|
||||
args = append(args, "--namespace", uninstallOpts.Namespace)
|
||||
}
|
||||
|
||||
_, err := hbpm.runWithKubeConfig("uninstall", args, uninstallOpts.KubernetesClusterAccess, uninstallOpts.Env)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to run helm uninstall on specified args")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -1,22 +1,11 @@
|
|||
package libhelm
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/binary"
|
||||
"github.com/portainer/portainer/pkg/libhelm/sdk"
|
||||
"github.com/portainer/portainer/pkg/libhelm/types"
|
||||
)
|
||||
|
||||
// HelmConfig is a struct that holds the configuration for the Helm package manager
|
||||
type HelmConfig struct {
|
||||
BinaryPath string `example:"/portainer/dist"`
|
||||
}
|
||||
|
||||
var errBinaryPathNotSpecified = errors.New("binary path not specified")
|
||||
|
||||
// NewHelmPackageManager returns a new instance of HelmPackageManager based on HelmConfig
|
||||
func NewHelmPackageManager(config HelmConfig) (HelmPackageManager, error) {
|
||||
if config.BinaryPath != "" {
|
||||
return binary.NewHelmBinaryPackageManager(config.BinaryPath), nil
|
||||
}
|
||||
return nil, errBinaryPathNotSpecified
|
||||
func NewHelmPackageManager() (types.HelmPackageManager, error) {
|
||||
return sdk.NewHelmSDKPackageManager(), nil
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ package options
|
|||
|
||||
// KubernetesClusterAccess represents core details which can be used to generate KubeConfig file/data
|
||||
type KubernetesClusterAccess struct {
|
||||
ClusterName string `example:"portainer-cluster-endpoint-1"`
|
||||
ContextName string `example:"portainer-ctx-endpoint-1"`
|
||||
UserName string `example:"portainer-user-endpoint-1"`
|
||||
ClusterServerURL string `example:"https://mycompany.k8s.com"`
|
||||
CertificateAuthorityFile string `example:"/data/tls/localhost.crt"`
|
||||
AuthToken string `example:"ey..."`
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/rs/zerolog/log"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/discovery/cached/memory"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/restmapper"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
// newRESTClientGetter creates a custom RESTClientGetter using the provided client config
|
||||
type clientConfigGetter struct {
|
||||
clientConfig clientcmd.ClientConfig
|
||||
namespace string
|
||||
}
|
||||
|
||||
// initActionConfig initializes the action configuration with kubernetes config
|
||||
func (hspm *HelmSDKPackageManager) initActionConfig(actionConfig *action.Configuration, namespace string, k8sAccess *options.KubernetesClusterAccess) error {
|
||||
// If namespace is not provided, use the default namespace
|
||||
if namespace == "" {
|
||||
namespace = "default"
|
||||
}
|
||||
|
||||
if k8sAccess == nil {
|
||||
// Use default kubeconfig
|
||||
settings := cli.New()
|
||||
clientGetter := settings.RESTClientGetter()
|
||||
return actionConfig.Init(clientGetter, namespace, "secret", hspm.logf)
|
||||
}
|
||||
|
||||
// Create client config
|
||||
configAPI := generateConfigAPI(namespace, k8sAccess)
|
||||
clientConfig := clientcmd.NewDefaultClientConfig(*configAPI, &clientcmd.ConfigOverrides{})
|
||||
|
||||
// Create a custom RESTClientGetter that uses our in-memory config
|
||||
clientGetter, err := newRESTClientGetter(clientConfig, namespace)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("cluster_name", k8sAccess.ClusterName).
|
||||
Str("cluster_url", k8sAccess.ClusterServerURL).
|
||||
Str("user_name", k8sAccess.UserName).
|
||||
Err(err).
|
||||
Msg("failed to create client getter")
|
||||
return err
|
||||
}
|
||||
|
||||
return actionConfig.Init(clientGetter, namespace, "secret", hspm.logf)
|
||||
}
|
||||
|
||||
// generateConfigAPI generates a new kubeconfig configuration
|
||||
func generateConfigAPI(namespace string, k8sAccess *options.KubernetesClusterAccess) *api.Config {
|
||||
// Create in-memory kubeconfig configuration
|
||||
configAPI := api.NewConfig()
|
||||
|
||||
// Create cluster
|
||||
cluster := api.NewCluster()
|
||||
cluster.Server = k8sAccess.ClusterServerURL
|
||||
|
||||
if k8sAccess.CertificateAuthorityFile != "" {
|
||||
// If we have a CA file, use it
|
||||
cluster.CertificateAuthority = k8sAccess.CertificateAuthorityFile
|
||||
} else {
|
||||
// Otherwise skip TLS verification
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
}
|
||||
|
||||
// Create auth info with token
|
||||
authInfo := api.NewAuthInfo()
|
||||
authInfo.Token = k8sAccess.AuthToken
|
||||
|
||||
// Create context
|
||||
context := api.NewContext()
|
||||
context.Cluster = k8sAccess.ClusterName
|
||||
context.AuthInfo = k8sAccess.UserName
|
||||
context.Namespace = namespace
|
||||
|
||||
// Add to config
|
||||
configAPI.Clusters[k8sAccess.ClusterName] = cluster
|
||||
configAPI.AuthInfos[k8sAccess.UserName] = authInfo
|
||||
configAPI.Contexts[k8sAccess.ContextName] = context
|
||||
configAPI.CurrentContext = k8sAccess.ContextName
|
||||
|
||||
return configAPI
|
||||
}
|
||||
|
||||
func newRESTClientGetter(clientConfig clientcmd.ClientConfig, namespace string) (*clientConfigGetter, error) {
|
||||
if clientConfig == nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Msg("client config is nil")
|
||||
|
||||
return nil, errors.New("client config provided during the helm client initialization was nil. Check the kubernetes cluster access configuration")
|
||||
}
|
||||
|
||||
return &clientConfigGetter{
|
||||
clientConfig: clientConfig,
|
||||
namespace: namespace,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToRESTConfig() (*rest.Config, error) {
|
||||
if c.clientConfig == nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Msg("client config is nil")
|
||||
|
||||
return nil, errors.New("client config provided during the helm client initialization was nil. Check the kubernetes cluster access configuration")
|
||||
}
|
||||
|
||||
return c.clientConfig.ClientConfig()
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error) {
|
||||
config, err := c.ToRESTConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the discovery client
|
||||
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Wrap the discovery client with a cached discovery client
|
||||
return memory.NewMemCacheClient(discoveryClient), nil
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToRESTMapper() (meta.RESTMapper, error) {
|
||||
discoveryClient, err := c.ToDiscoveryClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create a REST mapper from the discovery client
|
||||
return restmapper.NewDeferredDiscoveryRESTMapper(discoveryClient), nil
|
||||
}
|
||||
|
||||
func (c *clientConfigGetter) ToRawKubeConfigLoader() clientcmd.ClientConfig {
|
||||
return c.clientConfig
|
||||
}
|
||||
|
||||
// parseValues parses YAML values data into a map
|
||||
func (hspm *HelmSDKPackageManager) parseValues(data []byte) (map[string]any, error) {
|
||||
// Use Helm's built-in chartutil.ReadValues which properly handles the conversion
|
||||
// from map[interface{}]interface{} to map[string]interface{}
|
||||
return chartutil.ReadValues(data)
|
||||
}
|
||||
|
||||
// logf is a log helper function for Helm
|
||||
func (hspm *HelmSDKPackageManager) logf(format string, v ...any) {
|
||||
// Use zerolog for structured logging
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Msgf(format, v...)
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
|
||||
func Test_InitActionConfig(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
||||
t.Run("with nil k8sAccess should use default kubeconfig", func(t *testing.T) {
|
||||
actionConfig := new(action.Configuration)
|
||||
err := hspm.(*HelmSDKPackageManager).initActionConfig(actionConfig, "default", nil)
|
||||
|
||||
// The function should not fail by design, even when not running in a k8s environment
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
})
|
||||
|
||||
t.Run("with k8sAccess should create in-memory config", func(t *testing.T) {
|
||||
actionConfig := new(action.Configuration)
|
||||
k8sAccess := &options.KubernetesClusterAccess{
|
||||
ClusterServerURL: "https://kubernetes.default.svc",
|
||||
AuthToken: "test-token",
|
||||
}
|
||||
|
||||
// The function should not fail by design
|
||||
err := hspm.(*HelmSDKPackageManager).initActionConfig(actionConfig, "default", k8sAccess)
|
||||
is.NoError(err, "should not return error when using in-memory config")
|
||||
})
|
||||
|
||||
t.Run("with k8sAccess and CA file should create config with CA", func(t *testing.T) {
|
||||
actionConfig := new(action.Configuration)
|
||||
k8sAccess := &options.KubernetesClusterAccess{
|
||||
ClusterServerURL: "https://kubernetes.default.svc",
|
||||
AuthToken: "test-token",
|
||||
CertificateAuthorityFile: "/path/to/ca.crt",
|
||||
}
|
||||
|
||||
// The function should not fail by design
|
||||
err := hspm.(*HelmSDKPackageManager).initActionConfig(actionConfig, "default", k8sAccess)
|
||||
is.NoError(err, "should not return error when using in-memory config with CA")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ClientConfigGetter(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Create a mock client config
|
||||
configAPI := api.NewConfig()
|
||||
|
||||
// Create cluster
|
||||
cluster := api.NewCluster()
|
||||
cluster.Server = "https://kubernetes.default.svc"
|
||||
cluster.InsecureSkipTLSVerify = true
|
||||
|
||||
// Create auth info
|
||||
authInfo := api.NewAuthInfo()
|
||||
authInfo.Token = "test-token"
|
||||
|
||||
// Create context
|
||||
context := api.NewContext()
|
||||
context.Cluster = "test-cluster"
|
||||
context.AuthInfo = "test-user"
|
||||
context.Namespace = "default"
|
||||
|
||||
// Add to config
|
||||
configAPI.Clusters["test-cluster"] = cluster
|
||||
configAPI.AuthInfos["test-user"] = authInfo
|
||||
configAPI.Contexts["test-context"] = context
|
||||
configAPI.CurrentContext = "test-context"
|
||||
|
||||
clientConfig := clientcmd.NewDefaultClientConfig(*configAPI, &clientcmd.ConfigOverrides{})
|
||||
|
||||
// Create client config getter
|
||||
clientGetter, err := newRESTClientGetter(clientConfig, "default")
|
||||
is.NoError(err, "should not return error when creating client getter")
|
||||
|
||||
// Test ToRESTConfig
|
||||
restConfig, err := clientGetter.ToRESTConfig()
|
||||
is.NoError(err, "should not return error when creating REST config")
|
||||
is.NotNil(restConfig, "should return non-nil REST config")
|
||||
is.Equal("https://kubernetes.default.svc", restConfig.Host, "host should be https://kubernetes.default.svc")
|
||||
is.Equal("test-token", restConfig.BearerToken, "bearer token should be test-token")
|
||||
|
||||
// Test ToDiscoveryClient
|
||||
discoveryClient, err := clientGetter.ToDiscoveryClient()
|
||||
is.NoError(err, "should not return error when creating discovery client")
|
||||
is.NotNil(discoveryClient, "should return non-nil discovery client")
|
||||
|
||||
// Test ToRESTMapper
|
||||
restMapper, err := clientGetter.ToRESTMapper()
|
||||
is.NoError(err, "should not return error when creating REST mapper")
|
||||
is.NotNil(restMapper, "should return non-nil REST mapper")
|
||||
|
||||
// Test ToRawKubeConfigLoader
|
||||
config := clientGetter.ToRawKubeConfigLoader()
|
||||
is.NotNil(config, "should return non-nil config loader")
|
||||
}
|
||||
|
||||
func Test_ParseValues(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
||||
t.Run("should parse valid YAML values", func(t *testing.T) {
|
||||
yamlData := []byte(`
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
resources:
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
`)
|
||||
values, err := hspm.(*HelmSDKPackageManager).parseValues(yamlData)
|
||||
is.NoError(err, "should parse valid YAML without error")
|
||||
is.NotNil(values, "should return non-nil values")
|
||||
|
||||
// Verify structure
|
||||
service, ok := values["service"].(map[string]interface{})
|
||||
is.True(ok, "service should be a map")
|
||||
is.Equal("ClusterIP", service["type"], "service type should be ClusterIP")
|
||||
is.Equal(float64(80), service["port"], "service port should be 80")
|
||||
|
||||
resources, ok := values["resources"].(map[string]interface{})
|
||||
is.True(ok, "resources should be a map")
|
||||
limits, ok := resources["limits"].(map[string]interface{})
|
||||
is.True(ok, "limits should be a map")
|
||||
is.Equal("100m", limits["cpu"], "cpu limit should be 100m")
|
||||
is.Equal("128Mi", limits["memory"], "memory limit should be 128Mi")
|
||||
})
|
||||
|
||||
t.Run("should handle invalid YAML", func(t *testing.T) {
|
||||
yamlData := []byte(`
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
invalid yaml
|
||||
`)
|
||||
_, err := hspm.(*HelmSDKPackageManager).parseValues(yamlData)
|
||||
is.Error(err, "should return error for invalid YAML")
|
||||
})
|
||||
|
||||
t.Run("should handle empty YAML", func(t *testing.T) {
|
||||
yamlData := []byte(``)
|
||||
values, err := hspm.(*HelmSDKPackageManager).parseValues(yamlData)
|
||||
is.NoError(err, "should not return error for empty YAML")
|
||||
is.NotNil(values, "should return non-nil values for empty YAML")
|
||||
is.Len(values, 0, "should return empty map for empty YAML")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
"github.com/rs/zerolog/log"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
"helm.sh/helm/v3/pkg/downloader"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/postrender"
|
||||
)
|
||||
|
||||
// Install implements the HelmPackageManager interface by using the Helm SDK to install a chart.
|
||||
func (hspm *HelmSDKPackageManager) Install(installOpts options.InstallOptions) (*release.Release, error) {
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Str("name", installOpts.Name).
|
||||
Str("namespace", installOpts.Namespace).
|
||||
Str("repo", installOpts.Repo).
|
||||
Bool("wait", installOpts.Wait).
|
||||
Msg("Installing Helm chart")
|
||||
|
||||
if installOpts.Name == "" {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Str("name", installOpts.Name).
|
||||
Str("namespace", installOpts.Namespace).
|
||||
Str("repo", installOpts.Repo).
|
||||
Bool("wait", installOpts.Wait).
|
||||
Msg("Name is required for helm release installation")
|
||||
return nil, errors.New("name is required for helm release installation")
|
||||
}
|
||||
|
||||
// Initialize action configuration with kubernetes config
|
||||
actionConfig := new(action.Configuration)
|
||||
err := hspm.initActionConfig(actionConfig, installOpts.Namespace, installOpts.KubernetesClusterAccess)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Str("namespace", installOpts.Namespace).
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm configuration for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to initialize helm configuration for helm release installation")
|
||||
}
|
||||
|
||||
installClient, err := initInstallClient(actionConfig, installOpts)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm install client for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to initialize helm install client for helm release installation")
|
||||
}
|
||||
|
||||
values, err := hspm.GetHelmValuesFromFile(installOpts.ValuesFile)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to get Helm values from file for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to get Helm values from file for helm release installation")
|
||||
}
|
||||
|
||||
chart, err := hspm.loadAndValidateChart(installClient, installOpts)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to load and validate chart for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to load and validate chart for helm release installation")
|
||||
}
|
||||
|
||||
// Run the installation
|
||||
log.Info().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Str("name", installOpts.Name).
|
||||
Str("namespace", installOpts.Namespace).
|
||||
Msg("Running chart installation for helm release")
|
||||
|
||||
helmRelease, err := installClient.Run(chart, values)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Str("name", installOpts.Name).
|
||||
Str("namespace", installOpts.Namespace).
|
||||
Err(err).
|
||||
Msg("Failed to install helm chart for helm release installation")
|
||||
return nil, errors.Wrap(err, "helm was not able to install the chart for helm release installation")
|
||||
}
|
||||
|
||||
return &release.Release{
|
||||
Name: helmRelease.Name,
|
||||
Namespace: helmRelease.Namespace,
|
||||
Chart: release.Chart{
|
||||
Metadata: &release.Metadata{
|
||||
Name: helmRelease.Chart.Metadata.Name,
|
||||
Version: helmRelease.Chart.Metadata.Version,
|
||||
AppVersion: helmRelease.Chart.Metadata.AppVersion,
|
||||
},
|
||||
},
|
||||
Labels: helmRelease.Labels,
|
||||
Version: helmRelease.Version,
|
||||
Manifest: helmRelease.Manifest,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// loadAndValidateChart locates and loads the chart, and validates it.
|
||||
// it also checks for chart dependencies and updates them if necessary.
|
||||
// it returns the chart information.
|
||||
func (hspm *HelmSDKPackageManager) loadAndValidateChart(installClient *action.Install, installOpts options.InstallOptions) (*chart.Chart, error) {
|
||||
// Locate and load the chart
|
||||
chartPath, err := installClient.ChartPathOptions.LocateChart(installOpts.Chart, hspm.settings)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Err(err).
|
||||
Msg("Failed to locate chart for helm release installation")
|
||||
return nil, errors.Wrapf(err, "failed to find the helm chart at the path: %s/%s", installOpts.Repo, installOpts.Chart)
|
||||
}
|
||||
|
||||
chartReq, err := loader.Load(chartPath)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart_path", chartPath).
|
||||
Err(err).
|
||||
Msg("Failed to load chart for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to load chart for helm release installation")
|
||||
}
|
||||
|
||||
// Check chart dependencies to make sure all are present in /charts
|
||||
if chartDependencies := chartReq.Metadata.Dependencies; chartDependencies != nil {
|
||||
if err := action.CheckDependencies(chartReq, chartDependencies); err != nil {
|
||||
err = errors.Wrap(err, "failed to check chart dependencies for helm release installation")
|
||||
if !installClient.DependencyUpdate {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Msg("Updating chart dependencies for helm release installation")
|
||||
|
||||
providers := getter.All(hspm.settings)
|
||||
manager := &downloader.Manager{
|
||||
Out: os.Stdout,
|
||||
ChartPath: chartPath,
|
||||
Keyring: installClient.ChartPathOptions.Keyring,
|
||||
SkipUpdate: false,
|
||||
Getters: providers,
|
||||
RepositoryConfig: hspm.settings.RepositoryConfig,
|
||||
RepositoryCache: hspm.settings.RepositoryCache,
|
||||
Debug: hspm.settings.Debug,
|
||||
}
|
||||
if err := manager.Update(); err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", installOpts.Chart).
|
||||
Err(err).
|
||||
Msg("Failed to update chart dependencies for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to update chart dependencies for helm release installation")
|
||||
}
|
||||
|
||||
// Reload the chart with the updated Chart.lock file.
|
||||
if chartReq, err = loader.Load(chartPath); err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart_path", chartPath).
|
||||
Err(err).
|
||||
Msg("Failed to reload chart after dependency update for helm release installation")
|
||||
return nil, errors.Wrap(err, "failed to reload chart after dependency update for helm release installation")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return chartReq, nil
|
||||
}
|
||||
|
||||
// initInstallClient initializes the install client with the given options
|
||||
// and return the install client.
|
||||
func initInstallClient(actionConfig *action.Configuration, installOpts options.InstallOptions) (*action.Install, error) {
|
||||
installClient := action.NewInstall(actionConfig)
|
||||
installClient.CreateNamespace = true
|
||||
installClient.DependencyUpdate = true
|
||||
|
||||
installClient.ReleaseName = installOpts.Name
|
||||
installClient.Namespace = installOpts.Namespace
|
||||
installClient.ChartPathOptions.RepoURL = installOpts.Repo
|
||||
installClient.Wait = installOpts.Wait
|
||||
if installOpts.PostRenderer != "" {
|
||||
postRenderer, err := postrender.NewExec(installOpts.PostRenderer)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create post renderer")
|
||||
}
|
||||
installClient.PostRenderer = postRenderer
|
||||
}
|
||||
|
||||
return installClient, nil
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func createValuesFile(values string) (string, error) {
|
||||
file, err := os.CreateTemp("", "helm-values")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
_, err = file.WriteString(values)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return file.Name(), nil
|
||||
}
|
||||
|
||||
func Test_Install(t *testing.T) {
|
||||
test.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
|
||||
// Create a new SDK package manager
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
||||
t.Run("successfully installs nginx chart with name test-nginx", func(t *testing.T) {
|
||||
// SDK equivalent of: helm install test-nginx --repo https://kubernetes.github.io/ingress-nginx nginx
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx",
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
|
||||
release, err := hspm.Install(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: "test-nginx",
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
is.Equal("test-nginx", release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
})
|
||||
|
||||
t.Run("successfully installs nginx with values", func(t *testing.T) {
|
||||
// SDK equivalent of: helm install test-nginx-2 --repo https://kubernetes.github.io/ingress-nginx nginx --values /tmp/helm-values3161785816
|
||||
values, err := createValuesFile("service:\n port: 8081")
|
||||
is.NoError(err, "should create a values file")
|
||||
|
||||
defer os.Remove(values)
|
||||
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx-2",
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
ValuesFile: values,
|
||||
}
|
||||
release, err := hspm.Install(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: "test-nginx-2",
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
is.Equal("test-nginx-2", release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
})
|
||||
|
||||
t.Run("successfully installs portainer chart with name portainer-test", func(t *testing.T) {
|
||||
// SDK equivalent of: helm install portainer-test portainer --repo https://portainer.github.io/k8s/
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "portainer-test",
|
||||
Chart: "portainer",
|
||||
Repo: "https://portainer.github.io/k8s/",
|
||||
}
|
||||
release, err := hspm.Install(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: installOpts.Name,
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
is.Equal("portainer-test", release.Name, "release name should match")
|
||||
is.Equal(1, release.Version, "release version should be 1")
|
||||
is.NotEmpty(release.Manifest, "release manifest should not be empty")
|
||||
})
|
||||
|
||||
t.Run("install with values as string", func(t *testing.T) {
|
||||
// First create a values file since InstallOptions doesn't support values as string directly
|
||||
values, err := createValuesFile("service:\n port: 8082")
|
||||
is.NoError(err, "should create a values file")
|
||||
|
||||
defer os.Remove(values)
|
||||
|
||||
// Install with values file
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx-3",
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
ValuesFile: values,
|
||||
}
|
||||
release, err := hspm.Install(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: "test-nginx-3",
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
is.Equal("test-nginx-3", release.Name, "release name should match")
|
||||
})
|
||||
|
||||
t.Run("install with namespace", func(t *testing.T) {
|
||||
// Install with namespace
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx-4",
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
Namespace: "default",
|
||||
}
|
||||
release, err := hspm.Install(installOpts)
|
||||
if release != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: "test-nginx-4",
|
||||
Namespace: "default",
|
||||
})
|
||||
}
|
||||
|
||||
is.NoError(err, "should successfully install release")
|
||||
is.NotNil(release, "should return non-nil release")
|
||||
is.Equal("test-nginx-4", release.Name, "release name should match")
|
||||
is.Equal("default", release.Namespace, "release namespace should match")
|
||||
})
|
||||
|
||||
t.Run("returns an error when name is not provided", func(t *testing.T) {
|
||||
installOpts := options.InstallOptions{
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
_, err := hspm.Install(installOpts)
|
||||
|
||||
is.Error(err, "should return an error when name is not provided")
|
||||
is.Equal(err.Error(), "name is required")
|
||||
})
|
||||
|
||||
t.Run("install with invalid chart", func(t *testing.T) {
|
||||
// Install with invalid chart
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-invalid",
|
||||
Chart: "non-existent-chart",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
_, err := hspm.Install(installOpts)
|
||||
is.Error(err, "should return error when chart doesn't exist")
|
||||
is.Equal(err.Error(), "failed to find the helm chart at the path: https://kubernetes.github.io/ingress-nginx/non-existent-chart")
|
||||
})
|
||||
|
||||
t.Run("install with invalid repo", func(t *testing.T) {
|
||||
// Install with invalid repo
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-invalid-repo",
|
||||
Chart: "nginx",
|
||||
Repo: "https://non-existent-repo.example.com",
|
||||
}
|
||||
_, err := hspm.Install(installOpts)
|
||||
is.Error(err, "should return error when repo doesn't exist")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
"github.com/rs/zerolog/log"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
sdkrelease "helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
// List implements the HelmPackageManager interface by using the Helm SDK to list releases.
|
||||
// It returns a slice of ReleaseElement.
|
||||
func (hspm *HelmSDKPackageManager) List(listOpts options.ListOptions) ([]release.ReleaseElement, error) {
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("namespace", listOpts.Namespace).
|
||||
Str("filter", listOpts.Filter).
|
||||
Str("selector", listOpts.Selector).
|
||||
Msg("Listing Helm releases")
|
||||
|
||||
// Initialize action configuration with kubernetes config
|
||||
actionConfig := new(action.Configuration)
|
||||
err := hspm.initActionConfig(actionConfig, listOpts.Namespace, listOpts.KubernetesClusterAccess)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("namespace", listOpts.Namespace).
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm configuration")
|
||||
return nil, errors.Wrap(err, "failed to initialize helm configuration")
|
||||
}
|
||||
|
||||
listClient, err := initListClient(actionConfig, listOpts)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm list client")
|
||||
}
|
||||
|
||||
// Run the list operation
|
||||
releases, err := listClient.Run()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to list helm releases")
|
||||
return []release.ReleaseElement{}, errors.Wrap(err, "failed to list helm releases")
|
||||
}
|
||||
|
||||
// Convert from SDK release type to our release element type and return
|
||||
return convertToReleaseElements(releases), nil
|
||||
}
|
||||
|
||||
// convertToReleaseElements converts from the SDK release type to our release element type
|
||||
func convertToReleaseElements(releases []*sdkrelease.Release) []release.ReleaseElement {
|
||||
elements := make([]release.ReleaseElement, len(releases))
|
||||
|
||||
for i, rel := range releases {
|
||||
chartName := fmt.Sprintf("%s-%s", rel.Chart.Metadata.Name, rel.Chart.Metadata.Version)
|
||||
|
||||
elements[i] = release.ReleaseElement{
|
||||
Name: rel.Name,
|
||||
Namespace: rel.Namespace,
|
||||
Revision: strconv.Itoa(rel.Version),
|
||||
Updated: rel.Info.LastDeployed.String(),
|
||||
Status: string(rel.Info.Status),
|
||||
Chart: chartName,
|
||||
AppVersion: rel.Chart.Metadata.AppVersion,
|
||||
}
|
||||
}
|
||||
|
||||
return elements
|
||||
}
|
||||
|
||||
// initListClient initializes the list client with the given options
|
||||
// and return the list client.
|
||||
func initListClient(actionConfig *action.Configuration, listOpts options.ListOptions) (*action.List, error) {
|
||||
listClient := action.NewList(actionConfig)
|
||||
|
||||
// Configure list options
|
||||
if listOpts.Filter != "" {
|
||||
listClient.Filter = listOpts.Filter
|
||||
}
|
||||
|
||||
if listOpts.Selector != "" {
|
||||
listClient.Selector = listOpts.Selector
|
||||
}
|
||||
|
||||
// If no namespace is specified in options, list across all namespaces
|
||||
if listOpts.Namespace == "" {
|
||||
listClient.AllNamespaces = true
|
||||
}
|
||||
|
||||
// No limit by default
|
||||
listClient.Limit = 0
|
||||
// Show all releases, even if in a pending or failed state
|
||||
listClient.All = true
|
||||
|
||||
// Set state mask to ensure proper filtering by status
|
||||
listClient.SetStateMask()
|
||||
|
||||
return listClient, nil
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
"helm.sh/helm/v3/pkg/time"
|
||||
)
|
||||
|
||||
func Test_ConvertToReleaseElements(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Create mock releases
|
||||
releases := []*release.Release{
|
||||
{
|
||||
Name: "release1",
|
||||
Namespace: "default",
|
||||
Version: 1,
|
||||
Info: &release.Info{
|
||||
Status: release.StatusDeployed,
|
||||
LastDeployed: time.Now(),
|
||||
},
|
||||
Chart: &chart.Chart{
|
||||
Metadata: &chart.Metadata{
|
||||
Name: "chart1",
|
||||
Version: "1.0.0",
|
||||
AppVersion: "1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "release2",
|
||||
Namespace: "kube-system",
|
||||
Version: 2,
|
||||
Info: &release.Info{
|
||||
Status: release.StatusFailed,
|
||||
LastDeployed: time.Now(),
|
||||
},
|
||||
Chart: &chart.Chart{
|
||||
Metadata: &chart.Metadata{
|
||||
Name: "chart2",
|
||||
Version: "2.0.0",
|
||||
AppVersion: "2.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Convert to release elements
|
||||
elements := convertToReleaseElements(releases)
|
||||
|
||||
// Verify conversion
|
||||
is.Len(elements, 2, "should return 2 release elements")
|
||||
|
||||
// Verify first release
|
||||
is.Equal("release1", elements[0].Name, "first release name should be release1")
|
||||
is.Equal("default", elements[0].Namespace, "first release namespace should be default")
|
||||
is.Equal("1", elements[0].Revision, "first release revision should be 1")
|
||||
is.Equal(string(release.StatusDeployed), elements[0].Status, "first release status should be deployed")
|
||||
is.Equal("chart1-1.0.0", elements[0].Chart, "first release chart should be chart1-1.0.0")
|
||||
is.Equal("1.0.0", elements[0].AppVersion, "first release app version should be 1.0.0")
|
||||
|
||||
// Verify second release
|
||||
is.Equal("release2", elements[1].Name, "second release name should be release2")
|
||||
is.Equal("kube-system", elements[1].Namespace, "second release namespace should be kube-system")
|
||||
is.Equal("2", elements[1].Revision, "second release revision should be 2")
|
||||
is.Equal(string(release.StatusFailed), elements[1].Status, "second release status should be failed")
|
||||
is.Equal("chart2-2.0.0", elements[1].Chart, "second release chart should be chart2-2.0.0")
|
||||
is.Equal("2.0.0", elements[1].AppVersion, "second release app version should be 2.0.0")
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/types"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
)
|
||||
|
||||
// HelmSDKPackageManager is a wrapper for the helm SDK which implements HelmPackageManager
|
||||
type HelmSDKPackageManager struct {
|
||||
settings *cli.EnvSettings
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// NewHelmSDKPackageManager initializes a new HelmPackageManager service using the Helm SDK
|
||||
func NewHelmSDKPackageManager() types.HelmPackageManager {
|
||||
settings := cli.New()
|
||||
return &HelmSDKPackageManager{
|
||||
settings: settings,
|
||||
timeout: 300 * time.Second, // 5 minutes default timeout
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/types"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_NewHelmSDKPackageManager(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Test that NewHelmSDKPackageManager returns a non-nil HelmPackageManager
|
||||
manager := NewHelmSDKPackageManager()
|
||||
is.NotNil(manager, "should return non-nil HelmPackageManager")
|
||||
|
||||
// Test that the returned manager is of the correct type
|
||||
_, ok := manager.(*HelmSDKPackageManager)
|
||||
is.True(ok, "should return a *HelmSDKPackageManager")
|
||||
|
||||
// Test that the manager has the expected fields
|
||||
sdkManager := manager.(*HelmSDKPackageManager)
|
||||
is.NotNil(sdkManager.settings, "should have non-nil settings")
|
||||
is.Equal(300*time.Second, sdkManager.timeout, "should have 5 minute timeout")
|
||||
|
||||
// Test that the manager implements the HelmPackageManager interface
|
||||
var _ types.HelmPackageManager = manager
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/segmentio/encoding/json"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
var (
|
||||
errRequiredSearchOptions = errors.New("repo is required")
|
||||
errInvalidRepoURL = errors.New("the request failed since either the Helm repository was not found or the index.yaml is not valid")
|
||||
)
|
||||
|
||||
type RepoIndex struct {
|
||||
APIVersion string `json:"apiVersion"`
|
||||
Entries map[string][]ChartInfo `json:"entries"`
|
||||
Generated string `json:"generated"`
|
||||
}
|
||||
|
||||
// SearchRepo downloads the `index.yaml` file for specified repo, parses it and returns JSON to caller.
|
||||
func (hspm *HelmSDKPackageManager) SearchRepo(searchRepoOpts options.SearchRepoOptions) ([]byte, error) {
|
||||
// Validate input options
|
||||
if err := validateSearchRepoOptions(searchRepoOpts); err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("repo", searchRepoOpts.Repo).
|
||||
Err(err).
|
||||
Msg("Missing required search repo options")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("repo", searchRepoOpts.Repo).
|
||||
Msg("Searching repository")
|
||||
|
||||
// Parse and validate the repository URL
|
||||
repoURL, err := parseRepoURL(searchRepoOpts.Repo)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("repo", searchRepoOpts.Repo).
|
||||
Err(err).
|
||||
Msg("Invalid repository URL")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Set up Helm CLI environment
|
||||
repoSettings := cli.New()
|
||||
|
||||
// Ensure all required Helm directories exist
|
||||
if err := ensureHelmDirectoriesExist(repoSettings); err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to ensure Helm directories exist")
|
||||
return nil, errors.Wrap(err, "failed to ensure Helm directories exist")
|
||||
}
|
||||
|
||||
// Download the index file and update repository configuration
|
||||
indexPath, err := downloadRepoIndex(repoURL.String(), repoSettings, searchRepoOpts.Repo)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("repo_url", repoURL.String()).
|
||||
Err(err).
|
||||
Msg("Failed to download repository index")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Load and parse the index file
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("index_path", indexPath).
|
||||
Msg("Loading index file")
|
||||
|
||||
indexFile, err := loadIndexFile(indexPath)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("index_path", indexPath).
|
||||
Err(err).
|
||||
Msg("Failed to load index file")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Convert the index file to our response format
|
||||
result, err := convertIndexToResponse(indexFile)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to convert index to response format")
|
||||
return nil, errors.Wrap(err, "failed to convert index to response format")
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("repo", searchRepoOpts.Repo).
|
||||
Int("entries_count", len(indexFile.Entries)).
|
||||
Msg("Successfully searched repository")
|
||||
|
||||
return json.Marshal(result)
|
||||
}
|
||||
|
||||
// validateSearchRepoOptions validates the required search repository options.
|
||||
func validateSearchRepoOptions(opts options.SearchRepoOptions) error {
|
||||
if opts.Repo == "" {
|
||||
return errRequiredSearchOptions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseRepoURL parses and validates the repository URL.
|
||||
func parseRepoURL(repoURL string) (*url.URL, error) {
|
||||
parsedURL, err := url.ParseRequestURI(repoURL)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "invalid helm chart URL: "+repoURL)
|
||||
}
|
||||
return parsedURL, nil
|
||||
}
|
||||
|
||||
// downloadRepoIndex downloads the index.yaml file from the repository and updates
|
||||
// the repository configuration.
|
||||
func downloadRepoIndex(repoURLString string, repoSettings *cli.EnvSettings, repoName string) (string, error) {
|
||||
log.Debug().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("repo_url", repoURLString).
|
||||
Str("repo_name", repoName).
|
||||
Msg("Creating chart repository object")
|
||||
|
||||
// Create chart repository object
|
||||
rep, err := repo.NewChartRepository(
|
||||
&repo.Entry{
|
||||
URL: repoURLString,
|
||||
},
|
||||
getter.All(repoSettings),
|
||||
)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("repo_url", repoURLString).
|
||||
Err(err).
|
||||
Msg("Failed to create chart repository object")
|
||||
return "", errInvalidRepoURL
|
||||
}
|
||||
|
||||
// Load repository configuration file
|
||||
f, err := repo.LoadFile(repoSettings.RepositoryConfig)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("repo_config", repoSettings.RepositoryConfig).
|
||||
Err(err).
|
||||
Msg("Failed to load repo config")
|
||||
return "", errors.Wrap(err, "failed to load repo config")
|
||||
}
|
||||
|
||||
// Download the index file
|
||||
log.Debug().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("repo_url", repoURLString).
|
||||
Msg("Downloading index file")
|
||||
|
||||
indexPath, err := rep.DownloadIndexFile()
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("repo_url", repoURLString).
|
||||
Err(err).
|
||||
Msg("Failed to download index file")
|
||||
return "", errors.Wrapf(err, "looks like %q is not a valid chart repository or cannot be reached", repoURLString)
|
||||
}
|
||||
|
||||
// Update repository configuration
|
||||
c := repo.Entry{
|
||||
Name: repoName,
|
||||
URL: repoURLString,
|
||||
}
|
||||
f.Update(&c)
|
||||
|
||||
// Write updated configuration
|
||||
repoFile := repoSettings.RepositoryConfig
|
||||
if err := f.WriteFile(repoFile, 0644); err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("repo_file", repoSettings.RepositoryConfig).
|
||||
Err(err).
|
||||
Msg("Failed to write repository configuration")
|
||||
return "", errors.Wrap(err, "failed to write repository configuration")
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "helm_sdk_repo_index").
|
||||
Str("index_path", indexPath).
|
||||
Msg("Successfully downloaded index file")
|
||||
|
||||
return indexPath, nil
|
||||
}
|
||||
|
||||
// loadIndexFile loads the index file from the given path.
|
||||
func loadIndexFile(indexPath string) (*repo.IndexFile, error) {
|
||||
indexFile, err := repo.LoadIndexFile(indexPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to load downloaded index file: %s", indexPath)
|
||||
}
|
||||
return indexFile, nil
|
||||
}
|
||||
|
||||
// convertIndexToResponse converts the Helm index file to our response format.
|
||||
func convertIndexToResponse(indexFile *repo.IndexFile) (RepoIndex, error) {
|
||||
result := RepoIndex{
|
||||
APIVersion: indexFile.APIVersion,
|
||||
Entries: make(map[string][]ChartInfo),
|
||||
Generated: indexFile.Generated.String(),
|
||||
}
|
||||
|
||||
// Convert Helm SDK types to our response types
|
||||
for name, charts := range indexFile.Entries {
|
||||
result.Entries[name] = convertChartsToChartInfo(charts)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// convertChartsToChartInfo converts Helm chart entries to ChartInfo objects.
|
||||
func convertChartsToChartInfo(charts []*repo.ChartVersion) []ChartInfo {
|
||||
chartInfos := make([]ChartInfo, len(charts))
|
||||
for i, chart := range charts {
|
||||
chartInfos[i] = ChartInfo{
|
||||
Name: chart.Name,
|
||||
Version: chart.Version,
|
||||
AppVersion: chart.AppVersion,
|
||||
Description: chart.Description,
|
||||
Deprecated: chart.Deprecated,
|
||||
Created: chart.Created.String(),
|
||||
Digest: chart.Digest,
|
||||
Home: chart.Home,
|
||||
Sources: chart.Sources,
|
||||
URLs: chart.URLs,
|
||||
Icon: chart.Icon,
|
||||
Annotations: chart.Annotations,
|
||||
}
|
||||
}
|
||||
return chartInfos
|
||||
}
|
||||
|
||||
// ChartInfo represents a Helm chart in the repository index
|
||||
type ChartInfo struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
AppVersion string `json:"appVersion"`
|
||||
Description string `json:"description"`
|
||||
Deprecated bool `json:"deprecated"`
|
||||
Created string `json:"created"`
|
||||
Digest string `json:"digest"`
|
||||
Home string `json:"home"`
|
||||
Sources []string `json:"sources"`
|
||||
URLs []string `json:"urls"`
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Annotations any `json:"annotations,omitempty"`
|
||||
}
|
||||
|
||||
// ensureHelmDirectoriesExist checks and creates required Helm directories if they don't exist
|
||||
func ensureHelmDirectoriesExist(settings *cli.EnvSettings) error {
|
||||
log.Debug().
|
||||
Str("context", "helm_sdk_dirs").
|
||||
Msg("Ensuring Helm directories exist")
|
||||
|
||||
// List of directories to ensure exist
|
||||
directories := []string{
|
||||
filepath.Dir(settings.RepositoryConfig), // Repository config directory
|
||||
settings.RepositoryCache, // Repository cache directory
|
||||
filepath.Dir(settings.RegistryConfig), // Registry config directory
|
||||
settings.PluginsDirectory, // Plugins directory
|
||||
}
|
||||
|
||||
// Create each directory if it doesn't exist
|
||||
for _, dir := range directories {
|
||||
if dir == "" {
|
||||
continue // Skip empty paths
|
||||
}
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_dirs").
|
||||
Str("directory", dir).
|
||||
Err(err).
|
||||
Msg("Failed to create directory")
|
||||
return errors.Wrapf(err, "failed to create directory: %s", dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure registry config file exists
|
||||
if settings.RegistryConfig != "" {
|
||||
if _, err := os.Stat(settings.RegistryConfig); os.IsNotExist(err) {
|
||||
// Create the directory if it doesn't exist
|
||||
dir := filepath.Dir(settings.RegistryConfig)
|
||||
if err := os.MkdirAll(dir, 0700); err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_dirs").
|
||||
Str("directory", dir).
|
||||
Err(err).
|
||||
Msg("Failed to create directory")
|
||||
return errors.Wrapf(err, "failed to create directory: %s", dir)
|
||||
}
|
||||
|
||||
// Create an empty registry config file
|
||||
if _, err := os.Create(settings.RegistryConfig); err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_dirs").
|
||||
Str("file", settings.RegistryConfig).
|
||||
Err(err).
|
||||
Msg("Failed to create registry config file")
|
||||
return errors.Wrapf(err, "failed to create registry config file: %s", settings.RegistryConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure repository config file exists
|
||||
if settings.RepositoryConfig != "" {
|
||||
if _, err := os.Stat(settings.RepositoryConfig); os.IsNotExist(err) {
|
||||
// Create an empty repository config file with default yaml structure
|
||||
f := repo.NewFile()
|
||||
if err := f.WriteFile(settings.RepositoryConfig, 0644); err != nil {
|
||||
log.Error().
|
||||
Str("context", "helm_sdk_dirs").
|
||||
Str("file", settings.RepositoryConfig).
|
||||
Err(err).
|
||||
Msg("Failed to create repository config file")
|
||||
return errors.Wrapf(err, "failed to create repository config file: %s", settings.RepositoryConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "helm_sdk_dirs").
|
||||
Msg("Successfully ensured all Helm directories exist")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
url string
|
||||
invalid bool
|
||||
}
|
||||
|
||||
var tests = []testCase{
|
||||
{"not a helm repo", "https://portainer.io", true},
|
||||
{"ingress helm repo", "https://kubernetes.github.io/ingress-nginx", false},
|
||||
{"portainer helm repo", "https://portainer.github.io/k8s/", false},
|
||||
{"elastic helm repo with trailing slash", "https://helm.elastic.co/", false},
|
||||
{"lensesio helm repo without trailing slash", "https://lensesio.github.io/kafka-helm-charts", false},
|
||||
}
|
||||
|
||||
func Test_SearchRepo(t *testing.T) {
|
||||
is := assert.New(t)
|
||||
|
||||
// Create a new SDK package manager
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
||||
for _, test := range tests {
|
||||
func(tc testCase) {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
response, err := hspm.SearchRepo(options.SearchRepoOptions{Repo: tc.url})
|
||||
if tc.invalid {
|
||||
is.Errorf(err, "error expected: %s", tc.url)
|
||||
} else {
|
||||
is.NoError(err, "no error expected: %s", tc.url)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
is.NotEmpty(response, "response expected")
|
||||
}
|
||||
})
|
||||
}(test)
|
||||
}
|
||||
|
||||
t.Run("search repo with keyword", func(t *testing.T) {
|
||||
// Search for charts with keyword
|
||||
searchOpts := options.SearchRepoOptions{
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
responseBytes, err := hspm.SearchRepo(searchOpts)
|
||||
|
||||
// The function should not fail by design, even when not running in a k8s environment
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
is.NotNil(responseBytes, "should return non-nil response")
|
||||
is.NotEmpty(responseBytes, "should return non-empty response")
|
||||
|
||||
// Parse the ext response
|
||||
var repoIndex RepoIndex
|
||||
err = json.Unmarshal(responseBytes, &repoIndex)
|
||||
is.NoError(err, "should parse JSON response without error")
|
||||
is.NotEmpty(repoIndex, "should have at least one chart")
|
||||
|
||||
// Verify charts structure apiVersion, entries, generated
|
||||
is.Equal("v1", repoIndex.APIVersion, "apiVersion should be v1")
|
||||
is.NotEmpty(repoIndex.Entries, "entries should not be empty")
|
||||
is.NotEmpty(repoIndex.Generated, "generated should not be empty")
|
||||
|
||||
// there should be at least one chart
|
||||
is.Greater(len(repoIndex.Entries), 0, "should have at least one chart")
|
||||
})
|
||||
|
||||
t.Run("search repo with empty repo URL", func(t *testing.T) {
|
||||
// Search with empty repo URL
|
||||
searchOpts := options.SearchRepoOptions{
|
||||
Repo: "",
|
||||
}
|
||||
_, err := hspm.SearchRepo(searchOpts)
|
||||
is.Error(err, "should return error when repo URL is empty")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/rs/zerolog/log"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
)
|
||||
|
||||
var errRequiredShowOptions = errors.New("chart, repo and output format are required")
|
||||
|
||||
// Show implements the HelmPackageManager interface by using the Helm SDK to show chart information.
|
||||
// It supports showing chart values, readme, and chart details based on the provided ShowOptions.
|
||||
func (hspm *HelmSDKPackageManager) Show(showOpts options.ShowOptions) ([]byte, error) {
|
||||
if showOpts.Chart == "" || showOpts.Repo == "" || showOpts.OutputFormat == "" {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", showOpts.Chart).
|
||||
Str("repo", showOpts.Repo).
|
||||
Str("output_format", string(showOpts.OutputFormat)).
|
||||
Msg("Missing required show options")
|
||||
return nil, errRequiredShowOptions
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", showOpts.Chart).
|
||||
Str("repo", showOpts.Repo).
|
||||
Str("output_format", string(showOpts.OutputFormat)).
|
||||
Msg("Showing chart information")
|
||||
|
||||
// Initialize action configuration
|
||||
actionConfig := new(action.Configuration)
|
||||
if err := actionConfig.Init(nil, "", "", func(format string, v ...interface{}) {}); err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm configuration")
|
||||
return nil, fmt.Errorf("failed to initialize helm configuration: %w", err)
|
||||
}
|
||||
|
||||
// Create temporary directory for chart download
|
||||
tempDir, err := os.MkdirTemp("", "helm-show-*")
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to create temp directory")
|
||||
return nil, fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Create showClient action
|
||||
showClient, err := initShowClient(actionConfig, showOpts)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm show client")
|
||||
return nil, fmt.Errorf("failed to initialize helm show client: %w", err)
|
||||
}
|
||||
|
||||
// Locate and load the chart
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", showOpts.Chart).
|
||||
Str("repo", showOpts.Repo).
|
||||
Msg("Locating chart")
|
||||
|
||||
chartPath, err := showClient.ChartPathOptions.LocateChart(showOpts.Chart, hspm.settings)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", showOpts.Chart).
|
||||
Str("repo", showOpts.Repo).
|
||||
Err(err).
|
||||
Msg("Failed to locate chart")
|
||||
return nil, fmt.Errorf("failed to locate chart: %w", err)
|
||||
}
|
||||
|
||||
// Get the output based on the requested format
|
||||
output, err := showClient.Run(chartPath)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart_path", chartPath).
|
||||
Str("output_format", string(showOpts.OutputFormat)).
|
||||
Err(err).
|
||||
Msg("Failed to show chart info")
|
||||
return nil, fmt.Errorf("failed to show chart info: %w", err)
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("chart", showOpts.Chart).
|
||||
Int("output_size", len(output)).
|
||||
Msg("Successfully retrieved chart information")
|
||||
|
||||
return []byte(output), nil
|
||||
}
|
||||
|
||||
// initShowClient initializes the show client with the given options
|
||||
// and return the show client.
|
||||
func initShowClient(actionConfig *action.Configuration, showOpts options.ShowOptions) (*action.Show, error) {
|
||||
showClient := action.NewShowWithConfig(action.ShowAll, actionConfig)
|
||||
showClient.ChartPathOptions.RepoURL = showOpts.Repo
|
||||
showClient.ChartPathOptions.Version = "" // Latest version
|
||||
|
||||
// Set output type based on ShowOptions
|
||||
switch showOpts.OutputFormat {
|
||||
case options.ShowAll:
|
||||
showClient.OutputFormat = action.ShowAll
|
||||
case options.ShowChart:
|
||||
showClient.OutputFormat = action.ShowChart
|
||||
case options.ShowValues:
|
||||
showClient.OutputFormat = action.ShowValues
|
||||
case options.ShowReadme:
|
||||
showClient.OutputFormat = action.ShowReadme
|
||||
default:
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("output_format", string(showOpts.OutputFormat)).
|
||||
Msg("Unsupported output format")
|
||||
return nil, fmt.Errorf("unsupported output format: %s", showOpts.OutputFormat)
|
||||
}
|
||||
|
||||
return showClient, nil
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Show(t *testing.T) {
|
||||
test.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
|
||||
// Create a new SDK package manager
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
||||
// install the ingress-nginx chart to test the show command
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "ingress-nginx",
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
release, err := hspm.Install(installOpts)
|
||||
if release != nil || err != nil {
|
||||
defer hspm.Uninstall(options.UninstallOptions{
|
||||
Name: "ingress-nginx",
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("show requires chart, repo and output format", func(t *testing.T) {
|
||||
showOpts := options.ShowOptions{
|
||||
Chart: "",
|
||||
Repo: "",
|
||||
OutputFormat: "",
|
||||
}
|
||||
_, err := hspm.Show(showOpts)
|
||||
is.Error(err, "should return error when required options are missing")
|
||||
is.Contains(err.Error(), "chart, repo and output format are required", "error message should indicate required options")
|
||||
})
|
||||
|
||||
t.Run("show chart values", func(t *testing.T) {
|
||||
showOpts := options.ShowOptions{
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
OutputFormat: options.ShowValues,
|
||||
}
|
||||
values, err := hspm.Show(showOpts)
|
||||
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
is.NotEmpty(values, "should return non-empty values")
|
||||
})
|
||||
|
||||
t.Run("show chart readme", func(t *testing.T) {
|
||||
showOpts := options.ShowOptions{
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
OutputFormat: options.ShowReadme,
|
||||
}
|
||||
readme, err := hspm.Show(showOpts)
|
||||
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
is.NotEmpty(readme, "should return non-empty readme")
|
||||
})
|
||||
|
||||
t.Run("show chart definition", func(t *testing.T) {
|
||||
showOpts := options.ShowOptions{
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
OutputFormat: options.ShowChart,
|
||||
}
|
||||
chart, err := hspm.Show(showOpts)
|
||||
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
is.NotNil(chart, "should return non-nil chart definition")
|
||||
})
|
||||
|
||||
t.Run("show all chart info", func(t *testing.T) {
|
||||
showOpts := options.ShowOptions{
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
OutputFormat: options.ShowAll,
|
||||
}
|
||||
info, err := hspm.Show(showOpts)
|
||||
|
||||
is.NoError(err, "should not return error when not in k8s environment")
|
||||
is.NotEmpty(info, "should return non-empty chart info")
|
||||
})
|
||||
|
||||
t.Run("show with invalid output format", func(t *testing.T) {
|
||||
// Show with invalid output format
|
||||
showOpts := options.ShowOptions{
|
||||
Chart: "ingress-nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
OutputFormat: "invalid",
|
||||
}
|
||||
_, err := hspm.Show(showOpts)
|
||||
|
||||
is.Error(err, "should return error with invalid output format")
|
||||
is.Contains(err.Error(), "unsupported output format", "error message should indicate invalid output format")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/rs/zerolog/log"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
)
|
||||
|
||||
// Uninstall implements the HelmPackageManager interface by using the Helm SDK to uninstall a release.
|
||||
func (hspm *HelmSDKPackageManager) Uninstall(uninstallOpts options.UninstallOptions) error {
|
||||
if uninstallOpts.Name == "" {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Msg("Release name is required")
|
||||
return errors.New("release name is required")
|
||||
}
|
||||
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("release", uninstallOpts.Name).
|
||||
Str("namespace", uninstallOpts.Namespace).
|
||||
Msg("Uninstalling Helm release")
|
||||
|
||||
// Initialize action configuration with kubernetes config
|
||||
actionConfig := new(action.Configuration)
|
||||
err := hspm.initActionConfig(actionConfig, uninstallOpts.Namespace, uninstallOpts.KubernetesClusterAccess)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("release", uninstallOpts.Name).
|
||||
Str("namespace", uninstallOpts.Namespace).
|
||||
Err(err).
|
||||
Msg("Failed to initialize helm configuration")
|
||||
return errors.Wrap(err, "failed to initialize helm configuration")
|
||||
}
|
||||
|
||||
// Create uninstallClient action
|
||||
uninstallClient := action.NewUninstall(actionConfig)
|
||||
// 'foreground' means the parent object remains in a "terminating" state until all of its children are deleted. This ensures that all dependent resources are completely removed before finalizing the deletion of the parent resource.
|
||||
uninstallClient.DeletionPropagation = "foreground" // "background" or "orphan"
|
||||
|
||||
// Run the uninstallation
|
||||
log.Info().
|
||||
Str("context", "HelmClient").
|
||||
Str("release", uninstallOpts.Name).
|
||||
Str("namespace", uninstallOpts.Namespace).
|
||||
Msg("Running uninstallation")
|
||||
|
||||
result, err := uninstallClient.Run(uninstallOpts.Name)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("release", uninstallOpts.Name).
|
||||
Str("namespace", uninstallOpts.Namespace).
|
||||
Err(err).
|
||||
Msg("Failed to uninstall helm release")
|
||||
return errors.Wrap(err, "failed to uninstall helm release")
|
||||
}
|
||||
|
||||
if result != nil {
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("release", uninstallOpts.Name).
|
||||
Str("release_info", result.Release.Info.Description).
|
||||
Msg("Uninstall result details")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Uninstall(t *testing.T) {
|
||||
test.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
|
||||
// Create a new SDK package manager
|
||||
hspm := NewHelmSDKPackageManager()
|
||||
|
||||
t.Run("uninstall requires a release name", func(t *testing.T) {
|
||||
// Try to uninstall without a release name
|
||||
uninstallOpts := options.UninstallOptions{
|
||||
Name: "",
|
||||
}
|
||||
err := hspm.Uninstall(uninstallOpts)
|
||||
is.Error(err, "should return error when release name is empty")
|
||||
is.Contains(err.Error(), "release name is required", "error message should indicate release name is required")
|
||||
})
|
||||
|
||||
t.Run("uninstall a non-existent release", func(t *testing.T) {
|
||||
// Try to uninstall a release that doesn't exist
|
||||
uninstallOpts := options.UninstallOptions{
|
||||
Name: "non-existent-release",
|
||||
}
|
||||
err := hspm.Uninstall(uninstallOpts)
|
||||
|
||||
// The function should not fail by design, even when not running in a k8s environment
|
||||
// However, it should return an error for a non-existent release
|
||||
is.Error(err, "should return error when release doesn't exist")
|
||||
is.Contains(err.Error(), "not found", "error message should indicate release not found")
|
||||
})
|
||||
|
||||
// This test is commented out as it requires a real release to be installed first
|
||||
t.Run("successfully uninstall an existing release", func(t *testing.T) {
|
||||
// First install a release
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-uninstall",
|
||||
Chart: "nginx",
|
||||
Repo: "https://kubernetes.github.io/ingress-nginx",
|
||||
}
|
||||
|
||||
// Install the release
|
||||
_, err := hspm.Install(installOpts)
|
||||
if err != nil {
|
||||
t.Logf("Error installing release: %v", err)
|
||||
t.Skip("Skipping uninstall test because install failed")
|
||||
return
|
||||
}
|
||||
|
||||
// Now uninstall it
|
||||
uninstallOpts := options.UninstallOptions{
|
||||
Name: "test-uninstall",
|
||||
}
|
||||
err = hspm.Uninstall(uninstallOpts)
|
||||
is.NoError(err, "should successfully uninstall release")
|
||||
})
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package sdk
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
// GetHelmValuesFromFile reads the values file and parses it into a map[string]any
|
||||
// and returns the map.
|
||||
func (hspm *HelmSDKPackageManager) GetHelmValuesFromFile(valuesFile string) (map[string]any, error) {
|
||||
var vals map[string]any
|
||||
if valuesFile != "" {
|
||||
log.Debug().
|
||||
Str("context", "HelmClient").
|
||||
Str("values_file", valuesFile).
|
||||
Msg("Reading values file")
|
||||
|
||||
valuesData, err := os.ReadFile(valuesFile)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("values_file", valuesFile).
|
||||
Err(err).
|
||||
Msg("Failed to read values file")
|
||||
return nil, errors.Wrap(err, "failed to read values file")
|
||||
}
|
||||
|
||||
vals, err = hspm.parseValues(valuesData)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Str("context", "HelmClient").
|
||||
Str("values_file", valuesFile).
|
||||
Err(err).
|
||||
Msg("Failed to parse values file")
|
||||
return nil, errors.Wrap(err, "failed to parse values file")
|
||||
}
|
||||
}
|
||||
|
||||
return vals, nil
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package libhelmtest
|
||||
package test
|
||||
|
||||
import (
|
||||
"os"
|
|
@ -3,9 +3,9 @@ package test
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm"
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
"github.com/portainer/portainer/pkg/libhelm/types"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/encoding/json"
|
||||
|
@ -31,8 +31,8 @@ const (
|
|||
// Do not use this package for concurrent tests.
|
||||
type helmMockPackageManager struct{}
|
||||
|
||||
// NewMockHelmBinaryPackageManager initializes a new HelmPackageManager service (a mock instance)
|
||||
func NewMockHelmBinaryPackageManager(binaryPath string) libhelm.HelmPackageManager {
|
||||
// NewMockHelmPackageManager initializes a new HelmPackageManager service (a mock instance)
|
||||
func NewMockHelmPackageManager() types.HelmPackageManager {
|
||||
return &helmMockPackageManager{}
|
||||
}
|
||||
|
|
@ -1,16 +1,26 @@
|
|||
package libhelm
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/portainer/portainer/pkg/libhelm/options"
|
||||
"github.com/portainer/portainer/pkg/libhelm/release"
|
||||
"helm.sh/helm/v3/pkg/cli"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
// HelmPackageManager represents a service that interfaces with Helm
|
||||
type HelmPackageManager interface {
|
||||
Show(showOpts options.ShowOptions) ([]byte, error)
|
||||
SearchRepo(searchRepoOpts options.SearchRepoOptions) ([]byte, error)
|
||||
Get(getOpts options.GetOptions) ([]byte, error)
|
||||
List(listOpts options.ListOptions) ([]release.ReleaseElement, error)
|
||||
Install(installOpts options.InstallOptions) (*release.Release, error)
|
||||
Uninstall(uninstallOpts options.UninstallOptions) error
|
||||
}
|
||||
|
||||
type Repository interface {
|
||||
Charts() (repo.ChartVersions, error)
|
||||
}
|
||||
|
||||
type HelmRepo struct {
|
||||
Settings *cli.EnvSettings
|
||||
Orig *repo.Entry
|
||||
}
|
|
@ -15,6 +15,10 @@ func ValidateHelmRepositoryURL(repoUrl string, client *http.Client) error {
|
|||
return errors.New("URL is required")
|
||||
}
|
||||
|
||||
if strings.HasPrefix(repoUrl, "oci://") {
|
||||
return errors.New("OCI repositories are not supported yet")
|
||||
}
|
||||
|
||||
url, err := url.ParseRequestURI(repoUrl)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid helm repository URL '%s': %w", repoUrl, err)
|
||||
|
|
|
@ -3,12 +3,12 @@ package libhelm
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/portainer/pkg/libhelm/libhelmtest"
|
||||
"github.com/portainer/portainer/pkg/libhelm/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_ValidateHelmRepositoryURL(t *testing.T) {
|
||||
libhelmtest.EnsureIntegrationTest(t)
|
||||
test.EnsureIntegrationTest(t)
|
||||
is := assert.New(t)
|
||||
|
||||
type testCase struct {
|
||||
|
@ -26,7 +26,7 @@ func Test_ValidateHelmRepositoryURL(t *testing.T) {
|
|||
{"not helm repo", "http://google.com", true},
|
||||
{"not valid repo with trailing slash", "http://google.com/", true},
|
||||
{"not valid repo with trailing slashes", "http://google.com////", true},
|
||||
{"ingress helm repo", "https://kubernetes.github.io/ingress-nginx/", false},
|
||||
{"bitnami helm repo", "https://charts.bitnami.com/bitnami/", false},
|
||||
{"gitlap helm repo", "https://charts.gitlab.io/", false},
|
||||
{"portainer helm repo", "https://portainer.github.io/k8s/", false},
|
||||
{"elastic helm repo", "https://helm.elastic.co/", false},
|
||||
|
|
Loading…
Reference in New Issue