mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 07:49:41 +02:00
import libhelm into portainer (#8128)
This commit is contained in:
parent
241440a474
commit
d2f6d1e415
32 changed files with 1305 additions and 5 deletions
29
api/pkg/libhelm/binary/get.go
Normal file
29
api/pkg/libhelm/binary/get.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run helm get on specified args")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
58
api/pkg/libhelm/binary/helm_package.go
Normal file
58
api/pkg/libhelm/binary/helm_package.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os/exec"
|
||||
"path"
|
||||
"runtime"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/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) ([]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)
|
||||
}
|
||||
|
||||
// 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) ([]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
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, stderr.String())
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
48
api/pkg/libhelm/binary/install.go
Normal file
48
api/pkg/libhelm/binary/install.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/libhelm/options"
|
||||
"github.com/portainer/libhelm/release"
|
||||
)
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
118
api/pkg/libhelm/binary/install_test.go
Normal file
118
api/pkg/libhelm/binary/install_test.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/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://charts.bitnami.com/bitnami nginx
|
||||
installOpts := options.InstallOptions{
|
||||
Name: "test-nginx",
|
||||
Chart: "nginx",
|
||||
Repo: "https://charts.bitnami.com/bitnami",
|
||||
}
|
||||
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{"test-nginx"})
|
||||
|
||||
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://charts.bitnami.com/bitnami nginx
|
||||
installOpts := options.InstallOptions{
|
||||
Chart: "nginx",
|
||||
Repo: "https://charts.bitnami.com/bitnami",
|
||||
}
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{release.Name})
|
||||
|
||||
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://charts.bitnami.com/bitnami 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://charts.bitnami.com/bitnami",
|
||||
ValuesFile: values,
|
||||
}
|
||||
release, err := hbpm.Install(installOpts)
|
||||
defer hbpm.run("uninstall", []string{"test-nginx-2"})
|
||||
|
||||
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})
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
38
api/pkg/libhelm/binary/list.go
Normal file
38
api/pkg/libhelm/binary/list.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/libhelm/options"
|
||||
"github.com/portainer/libhelm/release"
|
||||
)
|
||||
|
||||
// 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)
|
||||
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
|
||||
}
|
84
api/pkg/libhelm/binary/search_repo.go
Normal file
84
api/pkg/libhelm/binary/search_repo.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
package binary
|
||||
|
||||
// Package common implements common functionality for the helm.
|
||||
// The functionality does not rely on the implementation of `HelmPackageManager`
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/libhelm/options"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var errRequiredSearchOptions = errors.New("repo is required")
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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: 60 * time.Second,
|
||||
}
|
||||
|
||||
url, err := url.ParseRequestURI(searchRepoOpts.Repo)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, fmt.Sprintf("invalid helm chart URL: %s", searchRepoOpts.Repo))
|
||||
}
|
||||
|
||||
url.Path = path.Join(url.Path, "index.yaml")
|
||||
resp, err := client.Get(url.String())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get index file")
|
||||
}
|
||||
|
||||
var file File
|
||||
err = yaml.NewDecoder(resp.Body).Decode(&file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode index file")
|
||||
}
|
||||
|
||||
result, err := json.Marshal(file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal index file")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
48
api/pkg/libhelm/binary/search_repo_test.go
Normal file
48
api/pkg/libhelm/binary/search_repo_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/portainer/libhelm/libhelmtest"
|
||||
"github.com/portainer/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},
|
||||
{"bitnami helm repo", "https://charts.bitnami.com/bitnami", 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},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
29
api/pkg/libhelm/binary/show.go
Normal file
29
api/pkg/libhelm/binary/show.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to run helm show on specified args")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
160
api/pkg/libhelm/binary/test/mock.go
Normal file
160
api/pkg/libhelm/binary/test/mock.go
Normal file
|
@ -0,0 +1,160 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/libhelm/options"
|
||||
"github.com/portainer/libhelm/release"
|
||||
"github.com/portainer/portainer/api/pkg/libhelm"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
MockDataIndex = "mock-index"
|
||||
MockDataChart = "mock-chart"
|
||||
MockDataReadme = "mock-readme"
|
||||
MockDataValues = "mock-values"
|
||||
)
|
||||
|
||||
const (
|
||||
MockReleaseHooks = "mock-release-hooks"
|
||||
MockReleaseManifest = "mock-release-manifest"
|
||||
MockReleaseNotes = "mock-release-notes"
|
||||
MockReleaseValues = "mock-release-values"
|
||||
)
|
||||
|
||||
// helmMockPackageManager is a test package for helm related http handler testing
|
||||
// Note: this package currently uses a slice in a way that is not thread safe.
|
||||
// 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 {
|
||||
return &helmMockPackageManager{}
|
||||
}
|
||||
|
||||
var mockCharts = []release.ReleaseElement{}
|
||||
|
||||
func newMockReleaseElement(installOpts options.InstallOptions) *release.ReleaseElement {
|
||||
return &release.ReleaseElement{
|
||||
Name: installOpts.Name,
|
||||
Namespace: installOpts.Namespace,
|
||||
Updated: "date/time",
|
||||
Status: "deployed",
|
||||
Chart: installOpts.Chart,
|
||||
AppVersion: "1.2.3",
|
||||
}
|
||||
}
|
||||
|
||||
func newMockRelease(re *release.ReleaseElement) *release.Release {
|
||||
return &release.Release{
|
||||
Name: re.Name,
|
||||
Namespace: re.Namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Install a helm chart (not thread safe)
|
||||
func (hpm *helmMockPackageManager) Install(installOpts options.InstallOptions) (*release.Release, error) {
|
||||
|
||||
releaseElement := newMockReleaseElement(installOpts)
|
||||
|
||||
// Enforce only one chart with the same name per namespace
|
||||
for i, rel := range mockCharts {
|
||||
if rel.Name == installOpts.Name && rel.Namespace == installOpts.Namespace {
|
||||
mockCharts[i] = *releaseElement
|
||||
return newMockRelease(releaseElement), nil
|
||||
}
|
||||
}
|
||||
|
||||
mockCharts = append(mockCharts, *releaseElement)
|
||||
return newMockRelease(releaseElement), nil
|
||||
}
|
||||
|
||||
// Show values/readme/chart etc
|
||||
func (hpm *helmMockPackageManager) Show(showOpts options.ShowOptions) ([]byte, error) {
|
||||
switch showOpts.OutputFormat {
|
||||
case options.ShowChart:
|
||||
return []byte(MockDataChart), nil
|
||||
case options.ShowReadme:
|
||||
return []byte(MockDataReadme), nil
|
||||
case options.ShowValues:
|
||||
return []byte(MockDataValues), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get release details - all, hooks, manifest, notes and values
|
||||
func (hpm *helmMockPackageManager) Get(getOpts options.GetOptions) ([]byte, error) {
|
||||
switch getOpts.ReleaseResource {
|
||||
case options.GetAll:
|
||||
return []byte(strings.Join([]string{MockReleaseHooks, MockReleaseManifest, MockReleaseNotes, MockReleaseValues}, "---\n")), nil
|
||||
case options.GetHooks:
|
||||
return []byte(MockReleaseHooks), nil
|
||||
case options.GetManifest:
|
||||
return []byte(MockReleaseManifest), nil
|
||||
case options.GetNotes:
|
||||
return []byte(MockReleaseNotes), nil
|
||||
case options.GetValues:
|
||||
return []byte(MockReleaseValues), nil
|
||||
default:
|
||||
return nil, errors.New("invalid release resource")
|
||||
}
|
||||
}
|
||||
|
||||
// Uninstall a helm chart (not thread safe)
|
||||
func (hpm *helmMockPackageManager) Uninstall(uninstallOpts options.UninstallOptions) error {
|
||||
for i, rel := range mockCharts {
|
||||
if rel.Name == uninstallOpts.Name && rel.Namespace == uninstallOpts.Namespace {
|
||||
mockCharts = append(mockCharts[:i], mockCharts[i+1:]...)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// List a helm chart (not thread safe)
|
||||
func (hpm *helmMockPackageManager) List(listOpts options.ListOptions) ([]release.ReleaseElement, error) {
|
||||
return mockCharts, nil
|
||||
}
|
||||
|
||||
const mockPortainerIndex = `apiVersion: v1
|
||||
entries:
|
||||
portainer:
|
||||
- apiVersion: v2
|
||||
appVersion: 2.0.0
|
||||
created: "2020-12-01T21:51:37.367634957Z"
|
||||
description: Helm chart used to deploy the Portainer for Kubernetes
|
||||
digest: f0e13dd3e7a05d17cb35c7879ffa623fd43b2c10ca968203e302b7a6c2764ddb
|
||||
home: https://www.portainer.io
|
||||
icon: https://github.com/portainer/portainer/raw/develop/app/assets/ico/apple-touch-icon.png
|
||||
maintainers:
|
||||
- email: davidy@funkypenguin.co.nz
|
||||
name: funkypenguin
|
||||
url: https://www.funkypenguin.co.nz
|
||||
name: portainer
|
||||
sources:
|
||||
- https://github.com/portainer/k8s
|
||||
type: application
|
||||
urls:
|
||||
- https://github.com/portainer/k8s/releases/download/portainer-1.0.6/portainer-1.0.6.tgz
|
||||
version: 1.0.6
|
||||
generated: "2020-08-19T00:00:46.754739363Z"`
|
||||
|
||||
func (hbpm *helmMockPackageManager) SearchRepo(searchRepoOpts options.SearchRepoOptions) ([]byte, error) {
|
||||
// Always return the same repo data no matter what
|
||||
reader := strings.NewReader(mockPortainerIndex)
|
||||
|
||||
var file release.File
|
||||
err := yaml.NewDecoder(reader).Decode(&file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to decode index file")
|
||||
}
|
||||
|
||||
result, err := json.Marshal(file)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to marshal index file")
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
29
api/pkg/libhelm/binary/uninstall.go
Normal file
29
api/pkg/libhelm/binary/uninstall.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package binary
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/portainer/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)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to run helm uninstall on specified args")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue