github.com/koderover/helm@v2.17.0+incompatible/pkg/tiller/release_modules.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 tiller
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"log"
    23  	"strings"
    24  
    25  	"k8s.io/client-go/kubernetes"
    26  
    27  	"k8s.io/helm/pkg/chartutil"
    28  	"k8s.io/helm/pkg/kube"
    29  	"k8s.io/helm/pkg/proto/hapi/release"
    30  	rudderAPI "k8s.io/helm/pkg/proto/hapi/rudder"
    31  	"k8s.io/helm/pkg/proto/hapi/services"
    32  	relutil "k8s.io/helm/pkg/releaseutil"
    33  	"k8s.io/helm/pkg/rudder"
    34  	"k8s.io/helm/pkg/tiller/environment"
    35  )
    36  
    37  // ReleaseModule is an interface that allows ReleaseServer to run operations on release via either local implementation or Rudder service
    38  type ReleaseModule interface {
    39  	Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error
    40  	Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error
    41  	Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error
    42  	Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error)
    43  	Delete(r *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (string, []error)
    44  }
    45  
    46  // LocalReleaseModule is a local implementation of ReleaseModule
    47  type LocalReleaseModule struct {
    48  	clientset kubernetes.Interface
    49  }
    50  
    51  // Create creates a release via kubeclient from provided environment
    52  func (m *LocalReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error {
    53  	b := bytes.NewBufferString(r.Manifest)
    54  	return env.KubeClient.Create(r.Namespace, b, req.Timeout, req.Wait)
    55  }
    56  
    57  // Update performs an update from current to target release
    58  func (m *LocalReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error {
    59  	c := bytes.NewBufferString(current.Manifest)
    60  	t := bytes.NewBufferString(target.Manifest)
    61  	return env.KubeClient.UpdateWithOptions(target.Namespace, c, t, kube.UpdateOptions{
    62  		Force:         req.Force,
    63  		Recreate:      req.Recreate,
    64  		Timeout:       req.Timeout,
    65  		ShouldWait:    req.Wait,
    66  		CleanupOnFail: req.CleanupOnFail,
    67  	})
    68  }
    69  
    70  // Rollback performs a rollback from current to target release
    71  func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error {
    72  	c := bytes.NewBufferString(current.Manifest)
    73  	t := bytes.NewBufferString(target.Manifest)
    74  	return env.KubeClient.UpdateWithOptions(target.Namespace, c, t, kube.UpdateOptions{
    75  		Force:         req.Force,
    76  		Recreate:      req.Recreate,
    77  		Timeout:       req.Timeout,
    78  		ShouldWait:    req.Wait,
    79  		CleanupOnFail: req.CleanupOnFail,
    80  	})
    81  }
    82  
    83  // Status returns kubectl-like formatted status of release objects
    84  func (m *LocalReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) {
    85  	return env.KubeClient.Get(r.Namespace, bytes.NewBufferString(r.Manifest))
    86  }
    87  
    88  // Delete deletes the release and returns manifests that were kept in the deletion process
    89  func (m *LocalReleaseModule) Delete(rel *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (kept string, errs []error) {
    90  	vs, err := GetVersionSet(m.clientset.Discovery())
    91  	if err != nil {
    92  		return rel.Manifest, []error{fmt.Errorf("Could not get apiVersions from Kubernetes: %v", err)}
    93  	}
    94  	return DeleteRelease(rel, vs, env.KubeClient)
    95  }
    96  
    97  // RemoteReleaseModule is a ReleaseModule which calls Rudder service to operate on a release
    98  type RemoteReleaseModule struct{}
    99  
   100  // Create calls rudder.InstallRelease
   101  func (m *RemoteReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error {
   102  	request := &rudderAPI.InstallReleaseRequest{Release: r}
   103  	_, err := rudder.InstallRelease(request)
   104  	return err
   105  }
   106  
   107  // Update calls rudder.UpgradeRelease
   108  func (m *RemoteReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error {
   109  	upgrade := &rudderAPI.UpgradeReleaseRequest{
   110  		Current:  current,
   111  		Target:   target,
   112  		Recreate: req.Recreate,
   113  		Timeout:  req.Timeout,
   114  		Wait:     req.Wait,
   115  		Force:    req.Force,
   116  	}
   117  	_, err := rudder.UpgradeRelease(upgrade)
   118  	return err
   119  }
   120  
   121  // Rollback calls rudder.Rollback
   122  func (m *RemoteReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error {
   123  	rollback := &rudderAPI.RollbackReleaseRequest{
   124  		Current:  current,
   125  		Target:   target,
   126  		Recreate: req.Recreate,
   127  		Timeout:  req.Timeout,
   128  		Wait:     req.Wait,
   129  	}
   130  	_, err := rudder.RollbackRelease(rollback)
   131  	return err
   132  }
   133  
   134  // Status returns status retrieved from rudder.ReleaseStatus
   135  func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) {
   136  	statusRequest := &rudderAPI.ReleaseStatusRequest{Release: r}
   137  	resp, err := rudder.ReleaseStatus(statusRequest)
   138  	if resp == nil {
   139  		return "", err
   140  	}
   141  	return resp.Info.Status.Resources, err
   142  }
   143  
   144  // Delete calls rudder.DeleteRelease
   145  func (m *RemoteReleaseModule) Delete(r *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (string, []error) {
   146  	deleteRequest := &rudderAPI.DeleteReleaseRequest{Release: r}
   147  	resp, err := rudder.DeleteRelease(deleteRequest)
   148  
   149  	errs := make([]error, 0)
   150  	result := ""
   151  
   152  	if err != nil {
   153  		errs = append(errs, err)
   154  	}
   155  	if resp != nil {
   156  		result = resp.Release.Manifest
   157  	}
   158  	return result, errs
   159  }
   160  
   161  // DeleteRelease is a helper that allows Rudder to delete a release without exposing most of Tiller inner functions
   162  func DeleteRelease(rel *release.Release, vs chartutil.VersionSet, kubeClient environment.KubeClient) (kept string, errs []error) {
   163  	manifests := relutil.SplitManifests(rel.Manifest)
   164  	_, files, err := sortManifests(manifests, vs, UninstallOrder)
   165  	if err != nil {
   166  		// We could instead just delete everything in no particular order.
   167  		// FIXME: One way to delete at this point would be to try a label-based
   168  		// deletion. The problem with this is that we could get a false positive
   169  		// and delete something that was not legitimately part of this release.
   170  		return rel.Manifest, []error{fmt.Errorf("corrupted release record. You must manually delete the resources: %s", err)}
   171  	}
   172  
   173  	filesToKeep, filesToDelete := filterManifestsToKeep(files)
   174  	if len(filesToKeep) > 0 {
   175  		kept = summarizeKeptManifests(filesToKeep, kubeClient, rel.Namespace)
   176  	}
   177  
   178  	errs = []error{}
   179  	for _, file := range filesToDelete {
   180  		b := bytes.NewBufferString(strings.TrimSpace(file.Content))
   181  		if b.Len() == 0 {
   182  			continue
   183  		}
   184  		if err := kubeClient.Delete(rel.Namespace, b); err != nil {
   185  			log.Printf("uninstall: Failed deletion of %q: %s", rel.Name, err)
   186  			if err == kube.ErrNoObjectsVisited {
   187  				// Rewrite the message from "no objects visited"
   188  				obj := ""
   189  				if file.Head != nil && file.Head.Metadata != nil {
   190  					obj = "[" + file.Head.Kind + "] " + file.Head.Metadata.Name
   191  				}
   192  				err = fmt.Errorf("release %q: object %q not found, skipping delete", rel.Name, obj)
   193  			}
   194  			errs = append(errs, err)
   195  		}
   196  	}
   197  	return kept, errs
   198  }