@ -17,32 +17,32 @@ limitations under the License.
package action
package action
import (
import (
"bytes"
"bytes"
"fmt"
"fmt"
"os"
"os"
"path"
"path"
"path/filepath"
"path/filepath"
"regexp"
"regexp"
"strings"
"strings"
"github.com/pkg/errors"
"github.com/pkg/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/client-go/discovery"
"k8s.io/client-go/discovery"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/rest"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/chartutil"
"helm.sh/helm/v3/pkg/engine"
"helm.sh/helm/v3/pkg/engine"
"helm.sh/helm/v3/pkg/kube"
"helm.sh/helm/v3/pkg/kube"
"helm.sh/helm/v3/pkg/postrender"
"helm.sh/helm/v3/pkg/postrender"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/registry"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/release"
"helm.sh/helm/v3/pkg/releaseutil"
"helm.sh/helm/v3/pkg/releaseutil"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage"
"helm.sh/helm/v3/pkg/storage/driver"
"helm.sh/helm/v3/pkg/storage/driver"
"helm.sh/helm/v3/pkg/time"
"helm.sh/helm/v3/pkg/time"
)
)
// Timestamper is a function capable of producing a timestamp.Timestamper.
// Timestamper is a function capable of producing a timestamp.Timestamper.
@ -52,14 +52,14 @@ import (
var Timestamper = time . Now
var Timestamper = time . Now
var (
var (
// errMissingChart indicates that a chart was not provided.
// errMissingChart indicates that a chart was not provided.
errMissingChart = errors . New ( "no chart provided" )
errMissingChart = errors . New ( "no chart provided" )
// errMissingRelease indicates that a release (name) was not provided.
// errMissingRelease indicates that a release (name) was not provided.
errMissingRelease = errors . New ( "no release provided" )
errMissingRelease = errors . New ( "no release provided" )
// errInvalidRevision indicates that an invalid release revision number was provided.
// errInvalidRevision indicates that an invalid release revision number was provided.
errInvalidRevision = errors . New ( "invalid release revision" )
errInvalidRevision = errors . New ( "invalid release revision" )
// errPending indicates that another instance of Helm is already applying an operation on a release.
// errPending indicates that another instance of Helm is already applying an operation on a release.
errPending = errors . New ( "another operation (install/upgrade/rollback) is in progress" )
errPending = errors . New ( "another operation (install/upgrade/rollback) is in progress" )
)
)
// ValidName is a regular expression for resource names.
// ValidName is a regular expression for resource names.
@ -79,22 +79,22 @@ var ValidName = regexp.MustCompile(`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-
// Configuration injects the dependencies that all actions share.
// Configuration injects the dependencies that all actions share.
type Configuration struct {
type Configuration struct {
// RESTClientGetter is an interface that loads Kubernetes clients.
// RESTClientGetter is an interface that loads Kubernetes clients.
RESTClientGetter RESTClientGetter
RESTClientGetter RESTClientGetter
// Releases stores records of releases.
// Releases stores records of releases.
Releases * storage . Storage
Releases * storage . Storage
// KubeClient is a Kubernetes API client.
// KubeClient is a Kubernetes API client.
KubeClient kube . Interface
KubeClient kube . Interface
// RegistryClient is a client for working with registries
// RegistryClient is a client for working with registries
RegistryClient * registry . Client
RegistryClient * registry . Client
// Capabilities describes the capabilities of the Kubernetes cluster.
// Capabilities describes the capabilities of the Kubernetes cluster.
Capabilities * chartutil . Capabilities
Capabilities * chartutil . Capabilities
Log func ( string , ... interface { } )
Log func ( string , ... interface { } )
}
}
// renderResources renders the templates in a chart
// renderResources renders the templates in a chart
@ -103,133 +103,133 @@ type Configuration struct {
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
// This code has to do with writing files to disk.
// This code has to do with writing files to disk.
func ( cfg * Configuration ) renderResources ( ch * chart . Chart , values chartutil . Values , releaseName , outputDir string , subNotes , useReleaseName , includeCrds bool , pr postrender . PostRenderer , dryRun bool ) ( [ ] * release . Hook , * bytes . Buffer , string , error ) {
func ( cfg * Configuration ) renderResources ( ch * chart . Chart , values chartutil . Values , releaseName , outputDir string , subNotes , useReleaseName , includeCrds bool , pr postrender . PostRenderer , dryRun bool ) ( [ ] * release . Hook , * bytes . Buffer , string , error ) {
hs := [ ] * release . Hook { }
hs := [ ] * release . Hook { }
b := bytes . NewBuffer ( nil )
b := bytes . NewBuffer ( nil )
caps , err := cfg . getCapabilities ( )
caps , err := cfg . getCapabilities ( )
if err != nil {
if err != nil {
return hs , b , "" , err
return hs , b , "" , err
}
}
if ch . Metadata . KubeVersion != "" {
if ch . Metadata . KubeVersion != "" {
if ! chartutil . IsCompatibleRange ( ch . Metadata . KubeVersion , caps . KubeVersion . String ( ) ) {
if ! chartutil . IsCompatibleRange ( ch . Metadata . KubeVersion , caps . KubeVersion . String ( ) ) {
return hs , b , "" , errors . Errorf ( "chart requires kubeVersion: %s which is incompatible with Kubernetes %s" , ch . Metadata . KubeVersion , caps . KubeVersion . String ( ) )
return hs , b , "" , errors . Errorf ( "chart requires kubeVersion: %s which is incompatible with Kubernetes %s" , ch . Metadata . KubeVersion , caps . KubeVersion . String ( ) )
}
}
}
}
var files map [ string ] string
var files map [ string ] string
var err2 error
var err2 error
// A `helm template` or `helm install --dry-run` should not talk to the remote cluster.
// A `helm template` or `helm install --dry-run` should not talk to the remote cluster.
// It will break in interesting and exotic ways because other data (e.g. discovery)
// It will break in interesting and exotic ways because other data (e.g. discovery)
// is mocked. It is not up to the template author to decide when the user wants to
// is mocked. It is not up to the template author to decide when the user wants to
// connect to the cluster. So when the user says to dry run, respect the user's
// connect to the cluster. So when the user says to dry run, respect the user's
// wishes and do not connect to the cluster.
// wishes and do not connect to the cluster.
if ! dryRun && cfg . RESTClientGetter != nil {
if ! dryRun && cfg . RESTClientGetter != nil {
restConfig , err := cfg . RESTClientGetter . ToRESTConfig ( )
restConfig , err := cfg . RESTClientGetter . ToRESTConfig ( )
if err != nil {
if err != nil {
return hs , b , "" , err
return hs , b , "" , err
}
}
files , err2 = engine . RenderWithClient ( ch , values , restConfig )
files , err2 = engine . RenderWithClient ( ch , values , restConfig )
} else {
} else {
files , err2 = engine . Render ( ch , values )
files , err2 = engine . Render ( ch , values )
}
}
if err2 != nil {
if err2 != nil {
return hs , b , "" , err2
return hs , b , "" , err2
}
}
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
// pull it out of here into a separate file so that we can actually use the output of the rendered
// pull it out of here into a separate file so that we can actually use the output of the rendered
// text file. We have to spin through this map because the file contains path information, so we
// text file. We have to spin through this map because the file contains path information, so we
// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
// look for terminating NOTES.txt. We also remove it from the files so that we don't have to skip
// it in the sortHooks.
// it in the sortHooks.
var notesBuffer bytes . Buffer
var notesBuffer bytes . Buffer
for k , v := range files {
for k , v := range files {
if strings . HasSuffix ( k , notesFileSuffix ) {
if strings . HasSuffix ( k , notesFileSuffix ) {
if subNotes || ( k == path . Join ( ch . Name ( ) , "templates" , notesFileSuffix ) ) {
if subNotes || ( k == path . Join ( ch . Name ( ) , "templates" , notesFileSuffix ) ) {
// If buffer contains data, add newline before adding more
// If buffer contains data, add newline before adding more
if notesBuffer . Len ( ) > 0 {
if notesBuffer . Len ( ) > 0 {
notesBuffer . WriteString ( "\n" )
notesBuffer . WriteString ( "\n" )
}
}
notesBuffer . WriteString ( v )
notesBuffer . WriteString ( v )
}
}
delete ( files , k )
delete ( files , k )
}
}
}
}
notes := notesBuffer . String ( )
notes := notesBuffer . String ( )
// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
// Sort hooks, manifests, and partials. Only hooks and manifests are returned,
// as partials are not used after renderer.Render. Empty manifests are also
// as partials are not used after renderer.Render. Empty manifests are also
// removed here.
// removed here.
hs , manifests , err := releaseutil . SortManifests ( files , caps . APIVersions , releaseutil . InstallOrder )
hs , manifests , err := releaseutil . SortManifests ( files , caps . APIVersions , releaseutil . InstallOrder )
if err != nil {
if err != nil {
// By catching parse errors here, we can prevent bogus releases from going
// By catching parse errors here, we can prevent bogus releases from going
// to Kubernetes.
// to Kubernetes.
//
//
// We return the files as a big blob of data to help the user debug parser
// We return the files as a big blob of data to help the user debug parser
// errors.
// errors.
for name , content := range files {
for name , content := range files {
if strings . TrimSpace ( content ) == "" {
if strings . TrimSpace ( content ) == "" {
continue
continue
}
}
fmt . Fprintf ( b , "---\n# Source: %s\n%s\n" , name , content )
fmt . Fprintf ( b , "---\n# Source: %s\n%s\n" , name , content )
}
}
return hs , b , "" , err
return hs , b , "" , err
}
}
// Aggregate all valid manifests into one big doc.
// Aggregate all valid manifests into one big doc.
fileWritten := make ( map [ string ] bool )
fileWritten := make ( map [ string ] bool )
if includeCrds {
if includeCrds {
for _ , crd := range ch . CRDObjects ( ) {
for _ , crd := range ch . CRDObjects ( ) {
if outputDir == "" {
if outputDir == "" {
fmt . Fprintf ( b , "---\n# Source: %s\n%s\n" , crd . Name , string ( crd . File . Data [ : ] ) )
fmt . Fprintf ( b , "---\n# Source: %s\n%s\n" , crd . Name , string ( crd . File . Data [ : ] ) )
} else {
} else {
err = writeToFile ( outputDir , crd . Filename , string ( crd . File . Data [ : ] ) , fileWritten [ crd . Name ] )
err = writeToFile ( outputDir , crd . Filename , string ( crd . File . Data [ : ] ) , fileWritten [ crd . Name ] )
if err != nil {
if err != nil {
return hs , b , "" , err
return hs , b , "" , err
}
}
fileWritten [ crd . Name ] = true
fileWritten [ crd . Name ] = true
}
}
}
}
}
}
for _ , m := range manifests {
for _ , m := range manifests {
if outputDir == "" {
if outputDir == "" {
fmt . Fprintf ( b , "---\n# Source: %s\n%s\n" , m . Name , m . Content )
fmt . Fprintf ( b , "---\n# Source: %s\n%s\n" , m . Name , m . Content )
} else {
} else {
newDir := outputDir
newDir := outputDir
if useReleaseName {
if useReleaseName {
newDir = filepath . Join ( outputDir , releaseName )
newDir = filepath . Join ( outputDir , releaseName )
}
}
// NOTE: We do not have to worry about the post-renderer because
// NOTE: We do not have to worry about the post-renderer because
// output dir is only used by `helm template`. In the next major
// output dir is only used by `helm template`. In the next major
// release, we should move this logic to template only as it is not
// release, we should move this logic to template only as it is not
// used by install or upgrade
// used by install or upgrade
err = writeToFile ( newDir , m . Name , m . Content , fileWritten [ m . Name ] )
err = writeToFile ( newDir , m . Name , m . Content , fileWritten [ m . Name ] )
if err != nil {
if err != nil {
return hs , b , "" , err
return hs , b , "" , err
}
}
fileWritten [ m . Name ] = true
fileWritten [ m . Name ] = true
}
}
}
}
if pr != nil {
if pr != nil {
b , err = pr . Run ( b )
b , err = pr . Run ( b )
if err != nil {
if err != nil {
return hs , b , notes , errors . Wrap ( err , "error while running post render on files" )
return hs , b , notes , errors . Wrap ( err , "error while running post render on files" )
}
}
}
}
return hs , b , notes , nil
return hs , b , notes , nil
}
}
// RESTClientGetter gets the rest client
// RESTClientGetter gets the rest client
type RESTClientGetter interface {
type RESTClientGetter interface {
ToRESTConfig ( ) ( * rest . Config , error )
ToRESTConfig ( ) ( * rest . Config , error )
ToDiscoveryClient ( ) ( discovery . CachedDiscoveryInterface , error )
ToDiscoveryClient ( ) ( discovery . CachedDiscoveryInterface , error )
ToRESTMapper ( ) ( meta . RESTMapper , error )
ToRESTMapper ( ) ( meta . RESTMapper , error )
}
}
// DebugLog sets the logger that writes debug strings
// DebugLog sets the logger that writes debug strings
@ -237,53 +237,54 @@ type DebugLog func(format string, v ...interface{})
// capabilities builds a Capabilities from discovery information.
// capabilities builds a Capabilities from discovery information.
func ( cfg * Configuration ) getCapabilities ( ) ( * chartutil . Capabilities , error ) {
func ( cfg * Configuration ) getCapabilities ( ) ( * chartutil . Capabilities , error ) {
if cfg . Capabilities != nil {
if cfg . Capabilities != nil {
return cfg . Capabilities , nil
return cfg . Capabilities , nil
}
}
dc , err := cfg . RESTClientGetter . ToDiscoveryClient ( )
dc , err := cfg . RESTClientGetter . ToDiscoveryClient ( )
if err != nil {
if err != nil {
return nil , errors . Wrap ( err , "could not get Kubernetes discovery client" )
return nil , errors . Wrap ( err , "could not get Kubernetes discovery client" )
}
}
// force a discovery cache invalidation to always fetch the latest server version/capabilities.
// force a discovery cache invalidation to always fetch the latest server version/capabilities.
dc . Invalidate ( )
dc . Invalidate ( )
kubeVersion , err := dc . ServerVersion ( )
kubeVersion , err := dc . ServerVersion ( )
if err != nil {
if err != nil {
return nil , errors . Wrap ( err , "could not get server version from Kubernetes" )
return nil , errors . Wrap ( err , "could not get server version from Kubernetes" )
}
}
// Issue #6361:
// Issue #6361:
// Client-Go emits an error when an API service is registered but unimplemented.
// Client-Go emits an error when an API service is registered but unimplemented.
// We trap that error here and print a warning. But since the discovery client continues
// We trap that error here and print a warning. But since the discovery client continues
// building the API object, it is correctly populated with all valid APIs.
// building the API object, it is correctly populated with all valid APIs.
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
apiVersions , err := GetVersionSet ( dc )
apiVersions , err := GetVersionSet ( dc )
if err != nil {
if err != nil {
if discovery . IsGroupDiscoveryFailedError ( err ) {
if discovery . IsGroupDiscoveryFailedError ( err ) {
cfg . Log ( "WARNING: The Kubernetes server has an orphaned API service. Server reports: %s" , err )
cfg . Log ( "WARNING: The Kubernetes server has an orphaned API service. Server reports: %s" , err )
cfg . Log ( "WARNING: To fix this, kubectl delete apiservice <service-name>" )
cfg . Log ( "WARNING: To fix this, kubectl delete apiservice <service-name>" )
} else {
} else {
return nil , errors . Wrap ( err , "could not get apiVersions from Kubernetes" )
return nil , errors . Wrap ( err , "could not get apiVersions from Kubernetes" )
}
}
}
}
cfg . Capabilities = & chartutil . Capabilities {
cfg . Capabilities = & chartutil . Capabilities {
APIVersions : apiVersions ,
APIVersions : apiVersions ,
KubeVersion : chartutil . KubeVersion {
KubeVersion : chartutil . KubeVersion {
Version : kubeVersion . GitVersion ,
Version : kubeVersion . GitVersion ,
Major : kubeVersion . Major ,
Major : kubeVersion . Major ,
Minor : kubeVersion . Minor ,
Minor : kubeVersion . Minor ,
} ,
} ,
}
HelmVersion : chartutil . DefaultCapabilities . HelmVersion ,
return cfg . Capabilities , nil
}
return cfg . Capabilities , nil
}
}
// KubernetesClientSet creates a new kubernetes ClientSet based on the configuration
// KubernetesClientSet creates a new kubernetes ClientSet based on the configuration
func ( cfg * Configuration ) KubernetesClientSet ( ) ( kubernetes . Interface , error ) {
func ( cfg * Configuration ) KubernetesClientSet ( ) ( kubernetes . Interface , error ) {
conf , err := cfg . RESTClientGetter . ToRESTConfig ( )
conf , err := cfg . RESTClientGetter . ToRESTConfig ( )
if err != nil {
if err != nil {
return nil , errors . Wrap ( err , "unable to generate config for kubernetes client" )
return nil , errors . Wrap ( err , "unable to generate config for kubernetes client" )
}
}
return kubernetes . NewForConfig ( conf )
return kubernetes . NewForConfig ( conf )
}
}
// Now generates a timestamp
// Now generates a timestamp
@ -291,130 +292,130 @@ func (cfg *Configuration) KubernetesClientSet() (kubernetes.Interface, error) {
// If the configuration has a Timestamper on it, that will be used.
// If the configuration has a Timestamper on it, that will be used.
// Otherwise, this will use time.Now().
// Otherwise, this will use time.Now().
func ( cfg * Configuration ) Now ( ) time . Time {
func ( cfg * Configuration ) Now ( ) time . Time {
return Timestamper ( )
return Timestamper ( )
}
}
func ( cfg * Configuration ) releaseContent ( name string , version int ) ( * release . Release , error ) {
func ( cfg * Configuration ) releaseContent ( name string , version int ) ( * release . Release , error ) {
if err := chartutil . ValidateReleaseName ( name ) ; err != nil {
if err := chartutil . ValidateReleaseName ( name ) ; err != nil {
return nil , errors . Errorf ( "releaseContent: Release name is invalid: %s" , name )
return nil , errors . Errorf ( "releaseContent: Release name is invalid: %s" , name )
}
}
if version <= 0 {
if version <= 0 {
return cfg . Releases . Last ( name )
return cfg . Releases . Last ( name )
}
}
return cfg . Releases . Get ( name , version )
return cfg . Releases . Get ( name , version )
}
}
// GetVersionSet retrieves a set of available k8s API versions
// GetVersionSet retrieves a set of available k8s API versions
func GetVersionSet ( client discovery . ServerResourcesInterface ) ( chartutil . VersionSet , error ) {
func GetVersionSet ( client discovery . ServerResourcesInterface ) ( chartutil . VersionSet , error ) {
groups , resources , err := client . ServerGroupsAndResources ( )
groups , resources , err := client . ServerGroupsAndResources ( )
if err != nil && ! discovery . IsGroupDiscoveryFailedError ( err ) {
if err != nil && ! discovery . IsGroupDiscoveryFailedError ( err ) {
return chartutil . DefaultVersionSet , errors . Wrap ( err , "could not get apiVersions from Kubernetes" )
return chartutil . DefaultVersionSet , errors . Wrap ( err , "could not get apiVersions from Kubernetes" )
}
}
// FIXME: The Kubernetes test fixture for cli appears to always return nil
// FIXME: The Kubernetes test fixture for cli appears to always return nil
// for calls to Discovery().ServerGroupsAndResources(). So in this case, we
// for calls to Discovery().ServerGroupsAndResources(). So in this case, we
// return the default API list. This is also a safe value to return in any
// return the default API list. This is also a safe value to return in any
// other odd-ball case.
// other odd-ball case.
if len ( groups ) == 0 && len ( resources ) == 0 {
if len ( groups ) == 0 && len ( resources ) == 0 {
return chartutil . DefaultVersionSet , nil
return chartutil . DefaultVersionSet , nil
}
}
versionMap := make ( map [ string ] interface { } )
versionMap := make ( map [ string ] interface { } )
versions := [ ] string { }
versions := [ ] string { }
// Extract the groups
// Extract the groups
for _ , g := range groups {
for _ , g := range groups {
for _ , gv := range g . Versions {
for _ , gv := range g . Versions {
versionMap [ gv . GroupVersion ] = struct { } { }
versionMap [ gv . GroupVersion ] = struct { } { }
}
}
}
}
// Extract the resources
// Extract the resources
var id string
var id string
var ok bool
var ok bool
for _ , r := range resources {
for _ , r := range resources {
for _ , rl := range r . APIResources {
for _ , rl := range r . APIResources {
// A Kind at a GroupVersion can show up more than once. We only want
// A Kind at a GroupVersion can show up more than once. We only want
// it displayed once in the final output.
// it displayed once in the final output.
id = path . Join ( r . GroupVersion , rl . Kind )
id = path . Join ( r . GroupVersion , rl . Kind )
if _ , ok = versionMap [ id ] ; ! ok {
if _ , ok = versionMap [ id ] ; ! ok {
versionMap [ id ] = struct { } { }
versionMap [ id ] = struct { } { }
}
}
}
}
}
}
// Convert to a form that NewVersionSet can use
// Convert to a form that NewVersionSet can use
for k := range versionMap {
for k := range versionMap {
versions = append ( versions , k )
versions = append ( versions , k )
}
}
return chartutil . VersionSet ( versions ) , nil
return chartutil . VersionSet ( versions ) , nil
}
}
// recordRelease with an update operation in case reuse has been set.
// recordRelease with an update operation in case reuse has been set.
func ( cfg * Configuration ) recordRelease ( r * release . Release ) {
func ( cfg * Configuration ) recordRelease ( r * release . Release ) {
if err := cfg . Releases . Update ( r ) ; err != nil {
if err := cfg . Releases . Update ( r ) ; err != nil {
cfg . Log ( "warning: Failed to update release %s: %s" , r . Name , err )
cfg . Log ( "warning: Failed to update release %s: %s" , r . Name , err )
}
}
}
}
// Init initializes the action configuration
// Init initializes the action configuration
func ( cfg * Configuration ) Init ( getter genericclioptions . RESTClientGetter , namespace , helmDriver string , log DebugLog ) error {
func ( cfg * Configuration ) Init ( getter genericclioptions . RESTClientGetter , namespace , helmDriver string , log DebugLog ) error {
kc := kube . New ( getter )
kc := kube . New ( getter )
kc . Log = log
kc . Log = log
lazyClient := & lazyClient {
lazyClient := & lazyClient {
namespace : namespace ,
namespace : namespace ,
clientFn : kc . Factory . KubernetesClientSet ,
clientFn : kc . Factory . KubernetesClientSet ,
}
}
var store * storage . Storage
var store * storage . Storage
switch helmDriver {
switch helmDriver {
case "secret" , "secrets" , "" :
case "secret" , "secrets" , "" :
d := driver . NewSecrets ( newSecretClient ( lazyClient ) )
d := driver . NewSecrets ( newSecretClient ( lazyClient ) )
d . Log = log
d . Log = log
store = storage . Init ( d )
store = storage . Init ( d )
case "configmap" , "configmaps" :
case "configmap" , "configmaps" :
d := driver . NewConfigMaps ( newConfigMapClient ( lazyClient ) )
d := driver . NewConfigMaps ( newConfigMapClient ( lazyClient ) )
d . Log = log
d . Log = log
store = storage . Init ( d )
store = storage . Init ( d )
case "memory" :
case "memory" :
var d * driver . Memory
var d * driver . Memory
if cfg . Releases != nil {
if cfg . Releases != nil {
if mem , ok := cfg . Releases . Driver . ( * driver . Memory ) ; ok {
if mem , ok := cfg . Releases . Driver . ( * driver . Memory ) ; ok {
// This function can be called more than once (e.g., helm list --all-namespaces).
// This function can be called more than once (e.g., helm list --all-namespaces).
// If a memory driver was already initialized, re-use it but set the possibly new namespace.
// If a memory driver was already initialized, re-use it but set the possibly new namespace.
// We re-use it in case some releases where already created in the existing memory driver.
// We re-use it in case some releases where already created in the existing memory driver.
d = mem
d = mem
}
}
}
}
if d == nil {
if d == nil {
d = driver . NewMemory ( )
d = driver . NewMemory ( )
}
}
d . SetNamespace ( namespace )
d . SetNamespace ( namespace )
store = storage . Init ( d )
store = storage . Init ( d )
case "sql" :
case "sql" :
d , err := driver . NewSQL (
d , err := driver . NewSQL (
os . Getenv ( "HELM_DRIVER_SQL_CONNECTION_STRING" ) ,
os . Getenv ( "HELM_DRIVER_SQL_CONNECTION_STRING" ) ,
log ,
log ,
namespace ,
namespace ,
)
)
if err != nil {
if err != nil {
panic ( fmt . Sprintf ( "Unable to instantiate SQL driver: %v" , err ) )
panic ( fmt . Sprintf ( "Unable to instantiate SQL driver: %v" , err ) )
}
}
store = storage . Init ( d )
store = storage . Init ( d )
default :
default :
// Not sure what to do here.
// Not sure what to do here.
panic ( "Unknown driver in HELM_DRIVER: " + helmDriver )
panic ( "Unknown driver in HELM_DRIVER: " + helmDriver )
}
}
cfg . RESTClientGetter = getter
cfg . RESTClientGetter = getter
cfg . KubeClient = kc
cfg . KubeClient = kc
cfg . Releases = store
cfg . Releases = store
cfg . Log = log
cfg . Log = log
return nil
return nil
}
}