github.com/umeshredd/helm@v3.0.0-alpha.1+incompatible/pkg/action/action.go (about)

     1  /*
     2  Copyright The Helm Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package action
    18  
    19  import (
    20  	"regexp"
    21  	"time"
    22  
    23  	"github.com/pkg/errors"
    24  	"k8s.io/apimachinery/pkg/api/meta"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/client-go/discovery"
    27  	"k8s.io/client-go/rest"
    28  
    29  	"helm.sh/helm/pkg/chartutil"
    30  	"helm.sh/helm/pkg/kube"
    31  	"helm.sh/helm/pkg/registry"
    32  	"helm.sh/helm/pkg/release"
    33  	"helm.sh/helm/pkg/storage"
    34  )
    35  
    36  // Timestamper is a function capable of producing a timestamp.Timestamper.
    37  //
    38  // By default, this is a time.Time function. This can be overridden for testing,
    39  // though, so that timestamps are predictable.
    40  var Timestamper = time.Now
    41  
    42  var (
    43  	// errMissingChart indicates that a chart was not provided.
    44  	errMissingChart = errors.New("no chart provided")
    45  	// errMissingRelease indicates that a release (name) was not provided.
    46  	errMissingRelease = errors.New("no release provided")
    47  	// errInvalidRevision indicates that an invalid release revision number was provided.
    48  	errInvalidRevision = errors.New("invalid release revision")
    49  	// errInvalidName indicates that an invalid release name was provided
    50  	errInvalidName = errors.New("invalid release name, must match regex ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$ and the length must not longer than 53")
    51  )
    52  
    53  // ValidName is a regular expression for names.
    54  //
    55  // According to the Kubernetes help text, the regular expression it uses is:
    56  //
    57  //	(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?
    58  //
    59  // We modified that. First, we added start and end delimiters. Second, we changed
    60  // the final ? to + to require that the pattern match at least once. This modification
    61  // prevents an empty string from matching.
    62  var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+$")
    63  
    64  // Configuration injects the dependencies that all actions share.
    65  type Configuration struct {
    66  	// RESTClientGetter is an interface that loads Kuberbetes clients.
    67  	RESTClientGetter RESTClientGetter
    68  
    69  	// Releases stores records of releases.
    70  	Releases *storage.Storage
    71  
    72  	// KubeClient is a Kubernetes API client.
    73  	KubeClient kube.Interface
    74  
    75  	// RegistryClient is a client for working with registries
    76  	RegistryClient *registry.Client
    77  
    78  	// Capabilities describes the capabilities of the Kubernetes cluster.
    79  	Capabilities *chartutil.Capabilities
    80  
    81  	Log func(string, ...interface{})
    82  }
    83  
    84  // capabilities builds a Capabilities from discovery information.
    85  func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
    86  	if c.Capabilities != nil {
    87  		return c.Capabilities, nil
    88  	}
    89  	dc, err := c.RESTClientGetter.ToDiscoveryClient()
    90  	if err != nil {
    91  		return nil, errors.Wrap(err, "could not get Kubernetes discovery client")
    92  	}
    93  	kubeVersion, err := dc.ServerVersion()
    94  	if err != nil {
    95  		return nil, errors.Wrap(err, "could not get server version from Kubernetes")
    96  	}
    97  	apiVersions, err := GetVersionSet(dc)
    98  	if err != nil {
    99  		return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
   100  	}
   101  
   102  	c.Capabilities = &chartutil.Capabilities{
   103  		APIVersions: apiVersions,
   104  		KubeVersion: chartutil.KubeVersion{
   105  			Version: kubeVersion.GitVersion,
   106  			Major:   kubeVersion.Major,
   107  			Minor:   kubeVersion.Minor,
   108  		},
   109  	}
   110  	return c.Capabilities, nil
   111  }
   112  
   113  // Now generates a timestamp
   114  //
   115  // If the configuration has a Timestamper on it, that will be used.
   116  // Otherwise, this will use time.Now().
   117  func (c *Configuration) Now() time.Time {
   118  	return Timestamper()
   119  }
   120  
   121  func (c *Configuration) releaseContent(name string, version int) (*release.Release, error) {
   122  	if err := validateReleaseName(name); err != nil {
   123  		return nil, errors.Errorf("releaseContent: Release name is invalid: %s", name)
   124  	}
   125  
   126  	if version <= 0 {
   127  		return c.Releases.Last(name)
   128  	}
   129  
   130  	return c.Releases.Get(name, version)
   131  }
   132  
   133  // GetVersionSet retrieves a set of available k8s API versions
   134  func GetVersionSet(client discovery.ServerGroupsInterface) (chartutil.VersionSet, error) {
   135  	groups, err := client.ServerGroups()
   136  	if err != nil {
   137  		return chartutil.DefaultVersionSet, err
   138  	}
   139  
   140  	// FIXME: The Kubernetes test fixture for cli appears to always return nil
   141  	// for calls to Discovery().ServerGroups(). So in this case, we return
   142  	// the default API list. This is also a safe value to return in any other
   143  	// odd-ball case.
   144  	if groups.Size() == 0 {
   145  		return chartutil.DefaultVersionSet, nil
   146  	}
   147  
   148  	versions := metav1.ExtractGroupVersions(groups)
   149  	return chartutil.VersionSet(versions), nil
   150  }
   151  
   152  // recordRelease with an update operation in case reuse has been set.
   153  func (c *Configuration) recordRelease(r *release.Release) {
   154  	if err := c.Releases.Update(r); err != nil {
   155  		c.Log("warning: Failed to update release %s: %s", r.Name, err)
   156  	}
   157  }
   158  
   159  type RESTClientGetter interface {
   160  	ToRESTConfig() (*rest.Config, error)
   161  	ToDiscoveryClient() (discovery.CachedDiscoveryInterface, error)
   162  	ToRESTMapper() (meta.RESTMapper, error)
   163  }