Skip to content

Commit

Permalink
Add helm service endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
akashshinde committed Jan 23, 2020
1 parent e702903 commit cdf6b18
Show file tree
Hide file tree
Showing 15 changed files with 1,052 additions and 33 deletions.
4 changes: 2 additions & 2 deletions cmd/bridge/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
"github.com/openshift/console/pkg/auth"
"github.com/openshift/console/pkg/bridge"
"github.com/openshift/console/pkg/crypto"
"github.com/openshift/console/pkg/helm"
"github.com/openshift/console/pkg/helm/chartproxy"
"github.com/openshift/console/pkg/proxy"
"github.com/openshift/console/pkg/server"
"github.com/openshift/console/pkg/serverconfig"
Expand Down Expand Up @@ -109,7 +109,7 @@ func main() {

fLoadTestFactor := fs.Int("load-test-factor", 0, "DEV ONLY. The factor used to multiply k8s API list responses for load testing purposes.")

helmConfig := helm.RegisterFlags(fs)
helmConfig := chartproxy.RegisterFlags(fs)

if err := fs.Parse(os.Args[1:]); err != nil {
fmt.Fprintln(os.Stderr, err.Error())
Expand Down
42 changes: 42 additions & 0 deletions pkg/helm/actions/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package actions

import (
"net/http"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/cli"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/rest"
"k8s.io/klog"
)

var settings = cli.New()

type configFlagsWithTransport struct {
*genericclioptions.ConfigFlags
Transport *http.RoundTripper
}

func (c configFlagsWithTransport) ToRESTConfig() (*rest.Config, error) {
return &rest.Config{
Host: *c.APIServer,
BearerToken: *c.BearerToken,
Transport: *c.Transport,
}, nil
}

func GetActionConfigurations(host, ns, token string, transport *http.RoundTripper) *action.Configuration {

confFlags := &configFlagsWithTransport{
ConfigFlags: &genericclioptions.ConfigFlags{
APIServer: &host,
BearerToken: &token,
Namespace: &ns,
},
Transport: transport,
}
conf := new(action.Configuration)
conf.Init(confFlags, ns, "secrets", klog.Infof)

return conf
}
34 changes: 34 additions & 0 deletions pkg/helm/actions/install_chart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package actions

import (
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/release"
)

func InstallChart(ns, name, url string, vals map[string]interface{}, conf *action.Configuration) (*release.Release, error) {
cmd := action.NewInstall(conf)

name, chart, err := cmd.NameAndChart([]string{name, url})
if err != nil {
return nil, err
}
cmd.ReleaseName = name

cp, err := cmd.ChartPathOptions.LocateChart(chart, settings)
if err != nil {
return nil, err
}

ch, err := loader.Load(cp)
if err != nil {
return nil, err
}

cmd.Namespace = ns
release, err := cmd.Run(ch, vals)
if err != nil {
return nil, err
}
return release, nil
}
71 changes: 71 additions & 0 deletions pkg/helm/actions/install_chart_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package actions

import (
"io/ioutil"
"testing"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chartutil"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"
)

func TestInstallChart(t *testing.T) {
tests := []struct {
releaseName string
chartPath string
chartName string
chartVersion string
}{
{
releaseName: "valid chart path",
chartPath: "../testdata/influxdb-3.0.2.tgz",
chartName: "influxdb",
chartVersion: "3.0.2",
},
{
releaseName: "invalid chart path",
chartPath: "../testdata/influxdb-3.0.1.tgz",
chartName: "influxdb",
chartVersion: "3.0.2",
},
}
for _, tt := range tests {
t.Run(tt.releaseName, func(t *testing.T) {
store := storage.Init(driver.NewMemory())
actionConfig := &action.Configuration{
Releases: store,
KubeClient: &kubefake.PrintingKubeClient{Out: ioutil.Discard},
Capabilities: chartutil.DefaultCapabilities,
Log: func(format string, v ...interface{}) {},
}
rel, err := InstallChart("test-namespace", "test", tt.chartPath, nil, actionConfig)
if tt.releaseName == "valid chart path" {
if err != nil {
t.Error("Error occurred while installing chartPath")
}
if rel.Name != "test" {
t.Error("Release releaseName isn't matching")
}
if rel.Namespace != "test-namespace" {
t.Error("Namespace releaseName isn't matching")
}
if rel.Info.Status != release.StatusDeployed {
t.Error("Chart status should be deployed")
}
if rel.Chart.Metadata.Name != tt.chartName {
t.Error("Chart name mismatch")
}
if rel.Chart.Metadata.Version != tt.chartVersion {
t.Error("Chart version mismatch")
}
} else if tt.releaseName == "invalid chart path" {
if err == nil {
t.Error("Should fail to parse while locating invalid chart")
}
}
})
}
}
20 changes: 20 additions & 0 deletions pkg/helm/actions/list_releases.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package actions

import (
"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/release"
)

func ListReleases(conf *action.Configuration) ([]*release.Release, error) {
cmd := action.NewList(conf)

releases, err := cmd.Run()
if err != nil {
return nil, err
}
if releases == nil {
rs := make([]*release.Release, 0)
return rs, nil
}
return releases, nil
}
75 changes: 75 additions & 0 deletions pkg/helm/actions/list_releases_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package actions

import (
"io/ioutil"
"testing"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
kubefake "helm.sh/helm/v3/pkg/kube/fake"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"
"helm.sh/helm/v3/pkg/time"
)

func TestListReleases(t *testing.T) {
tests := []struct {
name string
release release.Release
}{
{
name: "list valid releases",
release: release.Release{
Name: "test",
Namespace: "test-namespace",
Info: &release.Info{
FirstDeployed: time.Time{},
Status: "deployed",
},
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "influxdb",
Version: "3.0.2",
},
},
},
},
}
for _, tt := range tests {
store := storage.Init(driver.NewMemory())
t.Run(tt.name, func(t *testing.T) {
// create fake release
err := store.Create(&tt.release)
actionConfig := &action.Configuration{
Releases: store,
KubeClient: &kubefake.PrintingKubeClient{Out: ioutil.Discard},
Capabilities: chartutil.DefaultCapabilities,
Log: func(format string, v ...interface{}) {},
}
rels, err := ListReleases(actionConfig)
if err != nil {
t.Error("Error occurred while installing chartPath")
}
if len(rels) <= 0 {
t.Error("Release list should contain 1 release")
}
if rels[0].Name != "test" {
t.Error("Release releaseName isn't matching")
}
if rels[0].Namespace != "test-namespace" {
t.Error("Namespace rels[0]easeName isn't matching")
}
if rels[0].Info.Status != release.StatusDeployed {
t.Error("Chart status should be deployed")
}
if rels[0].Chart.Metadata.Name != "influxdb" {
t.Error("Chart name mismatch")
}
if rels[0].Chart.Metadata.Version != "3.0.2" {
t.Error("Chart version mismatch")
}
})
}
}
104 changes: 104 additions & 0 deletions pkg/helm/actions/template.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package actions

import (
"bytes"
"fmt"
"path/filepath"
"regexp"
"strings"

"helm.sh/helm/v3/pkg/action"
"helm.sh/helm/v3/pkg/chart/loader"
"helm.sh/helm/v3/pkg/cli"
"helm.sh/helm/v3/pkg/releaseutil"
)

func RenderManifests(name string, url string, vals map[string]interface{}, conf *action.Configuration) (string, error) {
var showFiles []string
response := make(map[string]string)
validate := false
client := action.NewInstall(conf)
client.DryRun = true
includeCrds := true
client.ReleaseName = "RELEASE-NAME"
client.Replace = true // Skip the releaseName check
client.ClientOnly = !validate
emptyResponse := ""

name, chart, err := client.NameAndChart([]string{name, url})
if err != nil {
return emptyResponse, err
}
client.ReleaseName = name

cp, err := client.ChartPathOptions.LocateChart(chart, cli.New())
if err != nil {
return emptyResponse, err
}

ch, err := loader.Load(cp)
if err != nil {
return emptyResponse, err
}

rel, err := client.Run(ch, vals)
if err != nil {
return emptyResponse, err
}

var manifests bytes.Buffer
var output bytes.Buffer

if includeCrds {
for _, f := range rel.Chart.CRDs() {
fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", f.Name, f.Data)
}
}

fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest))

if !client.DisableHooks {
for _, m := range rel.Hooks {
fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest)
}
}

// if we have a list of files to render, then check that each of the
// provided files exists in the chart.
if len(showFiles) > 0 {
splitManifests := releaseutil.SplitManifests(manifests.String())
manifestNameRegex := regexp.MustCompile("# Source: [^/]+/(.+)")
var manifestsToRender []string
for _, f := range showFiles {
missing := true
for _, manifest := range splitManifests {
submatch := manifestNameRegex.FindStringSubmatch(manifest)
if len(submatch) == 0 {
continue
}
manifestName := submatch[1]
// manifest.Name is rendered using linux-style filepath separators on Windows as
// well as macOS/linux.
manifestPathSplit := strings.Split(manifestName, "/")
manifestPath := filepath.Join(manifestPathSplit...)

// if the filepath provided matches a manifest path in the
// chart, render that manifest
if f == manifestPath {
manifestsToRender = append(manifestsToRender, manifest)
missing = false
}
}
if missing {
return "", fmt.Errorf("could not find template %s in chart", f)
}
for _, m := range manifestsToRender {
response[f] = m
fmt.Fprintf(&output, "---\n%s\n", m)
}
}
} else {
fmt.Fprintf(&output, "%s", manifests.String())
}
return output.String(), nil
}

0 comments on commit cdf6b18

Please sign in to comment.