github.com/koderover/helm@v2.17.0+incompatible/pkg/helm/client.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 helm // import "k8s.io/helm/pkg/helm"
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"time"
    23  
    24  	"golang.org/x/net/context"
    25  	"google.golang.org/grpc"
    26  	"google.golang.org/grpc/credentials"
    27  	"google.golang.org/grpc/keepalive"
    28  
    29  	healthpb "google.golang.org/grpc/health/grpc_health_v1"
    30  	"k8s.io/helm/pkg/chartutil"
    31  	"k8s.io/helm/pkg/proto/hapi/chart"
    32  	rls "k8s.io/helm/pkg/proto/hapi/services"
    33  )
    34  
    35  // maxMsgSize use 20MB as the default message size limit.
    36  // grpc library default is 4MB
    37  const maxMsgSize = 1024 * 1024 * 20
    38  
    39  // Client manages client side of the Helm-Tiller protocol.
    40  type Client struct {
    41  	opts options
    42  }
    43  
    44  // NewClient creates a new client.
    45  func NewClient(opts ...Option) *Client {
    46  	var c Client
    47  	// set some sane defaults
    48  	c.Option(ConnectTimeout(5))
    49  	return c.Option(opts...)
    50  }
    51  
    52  // Option configures the Helm client with the provided options.
    53  func (h *Client) Option(opts ...Option) *Client {
    54  	for _, opt := range opts {
    55  		opt(&h.opts)
    56  	}
    57  	return h
    58  }
    59  
    60  // ListReleases lists the current releases.
    61  func (h *Client) ListReleases(opts ...ReleaseListOption) (*rls.ListReleasesResponse, error) {
    62  	reqOpts := h.opts
    63  	for _, opt := range opts {
    64  		opt(&reqOpts)
    65  	}
    66  	req := &reqOpts.listReq
    67  	ctx := NewContext()
    68  
    69  	if reqOpts.before != nil {
    70  		if err := reqOpts.before(ctx, req); err != nil {
    71  			return nil, err
    72  		}
    73  	}
    74  	return h.list(ctx, req)
    75  }
    76  
    77  // InstallRelease loads a chart from chstr, installs it, and returns the release response.
    78  func (h *Client) InstallRelease(chstr, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
    79  	// load the chart to install
    80  	chart, err := chartutil.Load(chstr)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	return h.InstallReleaseFromChart(chart, ns, opts...)
    86  }
    87  
    88  // InstallReleaseWithContext loads a chart from chstr, installs it, and returns the release response while accepting a context.
    89  func (h *Client) InstallReleaseWithContext(ctx context.Context, chstr, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
    90  	// load the chart to install
    91  	chart, err := chartutil.Load(chstr)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	return h.installReleaseFromChartWithContext(ctx, chart, ns, opts...)
    97  }
    98  
    99  // InstallReleaseFromChart installs a new chart and returns the release response.
   100  func (h *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
   101  	return h.installReleaseFromChartWithContext(NewContext(), chart, ns, opts...)
   102  }
   103  
   104  // InstallReleaseFromChartWithContext installs a new chart and returns the release response while accepting a context.
   105  func (h *Client) InstallReleaseFromChartWithContext(ctx context.Context, chart *chart.Chart, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
   106  	return h.installReleaseFromChartWithContext(ctx, chart, ns, opts...)
   107  }
   108  
   109  // InstallReleaseFromChartWithContext installs a new chart and returns the release response while accepting a context.
   110  func (h *Client) installReleaseFromChartWithContext(ctx context.Context, chart *chart.Chart, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
   111  	// apply the install options
   112  	reqOpts := h.opts
   113  	for _, opt := range opts {
   114  		opt(&reqOpts)
   115  	}
   116  	req := &reqOpts.instReq
   117  	req.Chart = chart
   118  	req.Namespace = ns
   119  	req.DryRun = reqOpts.dryRun
   120  	req.DisableHooks = reqOpts.disableHooks
   121  	req.DisableCrdHook = reqOpts.disableCRDHook
   122  	req.ReuseName = reqOpts.reuseName
   123  	ctx = FromContext(ctx)
   124  
   125  	if reqOpts.before != nil {
   126  		if err := reqOpts.before(ctx, req); err != nil {
   127  			return nil, err
   128  		}
   129  	}
   130  	err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values)
   131  	if err != nil {
   132  		return nil, err
   133  	}
   134  	err = chartutil.ProcessRequirementsImportValues(req.Chart)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  
   139  	return h.install(ctx, req)
   140  }
   141  
   142  // DeleteRelease uninstalls a named release and returns the response.
   143  func (h *Client) DeleteRelease(rlsName string, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) {
   144  	// apply the uninstall options
   145  	reqOpts := h.opts
   146  	for _, opt := range opts {
   147  		opt(&reqOpts)
   148  	}
   149  
   150  	if reqOpts.dryRun {
   151  		// In the dry run case, just see if the release exists
   152  		r, err := h.ReleaseContent(rlsName)
   153  		if err != nil {
   154  			return &rls.UninstallReleaseResponse{}, err
   155  		}
   156  		return &rls.UninstallReleaseResponse{Release: r.Release}, nil
   157  	}
   158  
   159  	req := &reqOpts.uninstallReq
   160  	req.Name = rlsName
   161  	req.DisableHooks = reqOpts.disableHooks
   162  	ctx := NewContext()
   163  
   164  	if reqOpts.before != nil {
   165  		if err := reqOpts.before(ctx, req); err != nil {
   166  			return nil, err
   167  		}
   168  	}
   169  	return h.delete(ctx, req)
   170  }
   171  
   172  // UpdateRelease loads a chart from chstr and updates a release to a new/different chart.
   173  func (h *Client) UpdateRelease(rlsName string, chstr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   174  	// load the chart to update
   175  	chart, err := chartutil.Load(chstr)
   176  	if err != nil {
   177  		return nil, err
   178  	}
   179  
   180  	return h.UpdateReleaseFromChart(rlsName, chart, opts...)
   181  }
   182  
   183  // UpdateReleaseWithContext loads a chart from chstr and updates a release to a new/different chart while accepting a context.
   184  func (h *Client) UpdateReleaseWithContext(ctx context.Context, rlsName string, chstr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   185  	// load the chart to update
   186  	chart, err := chartutil.Load(chstr)
   187  	if err != nil {
   188  		return nil, err
   189  	}
   190  
   191  	return h.updateReleaseFromChartWithContext(ctx, rlsName, chart, opts...)
   192  }
   193  
   194  // UpdateReleaseFromChart updates a release to a new/different chart.
   195  func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   196  	return h.updateReleaseFromChartWithContext(NewContext(), rlsName, chart, opts...)
   197  }
   198  
   199  // UpdateReleaseFromChartWithContext updates a release to a new/different chart while accepting a context.
   200  func (h *Client) UpdateReleaseFromChartWithContext(ctx context.Context, rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   201  	return h.updateReleaseFromChartWithContext(ctx, rlsName, chart, opts...)
   202  }
   203  
   204  // updateReleaseFromChartWithContext updates a release to a new/different chart and accepts a context.
   205  func (h *Client) updateReleaseFromChartWithContext(ctx context.Context, rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   206  	// apply the update options
   207  	reqOpts := h.opts
   208  	for _, opt := range opts {
   209  		opt(&reqOpts)
   210  	}
   211  	req := &reqOpts.updateReq
   212  	req.Chart = chart
   213  	req.DryRun = reqOpts.dryRun
   214  	req.Name = rlsName
   215  	req.DisableHooks = reqOpts.disableHooks
   216  	req.Recreate = reqOpts.recreate
   217  	req.Force = reqOpts.force
   218  	req.ResetValues = reqOpts.resetValues
   219  	req.ReuseValues = reqOpts.reuseValues
   220  	ctx = FromContext(ctx)
   221  
   222  	if reqOpts.before != nil {
   223  		if err := reqOpts.before(ctx, req); err != nil {
   224  			return nil, err
   225  		}
   226  	}
   227  	err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values)
   228  	if err != nil {
   229  		return nil, err
   230  	}
   231  	err = chartutil.ProcessRequirementsImportValues(req.Chart)
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	return h.update(ctx, req)
   237  }
   238  
   239  // GetVersion returns the server version.
   240  func (h *Client) GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error) {
   241  	reqOpts := h.opts
   242  	for _, opt := range opts {
   243  		opt(&reqOpts)
   244  	}
   245  	req := &rls.GetVersionRequest{}
   246  	ctx := NewContext()
   247  
   248  	if reqOpts.before != nil {
   249  		if err := reqOpts.before(ctx, req); err != nil {
   250  			return nil, err
   251  		}
   252  	}
   253  	return h.version(ctx, req)
   254  }
   255  
   256  // RollbackRelease rolls back a release to the previous version.
   257  func (h *Client) RollbackRelease(rlsName string, opts ...RollbackOption) (*rls.RollbackReleaseResponse, error) {
   258  	reqOpts := h.opts
   259  	for _, opt := range opts {
   260  		opt(&reqOpts)
   261  	}
   262  	req := &reqOpts.rollbackReq
   263  	req.Recreate = reqOpts.recreate
   264  	req.Force = reqOpts.force
   265  	req.DisableHooks = reqOpts.disableHooks
   266  	req.DryRun = reqOpts.dryRun
   267  	req.Name = rlsName
   268  	ctx := NewContext()
   269  
   270  	if reqOpts.before != nil {
   271  		if err := reqOpts.before(ctx, req); err != nil {
   272  			return nil, err
   273  		}
   274  	}
   275  	return h.rollback(ctx, req)
   276  }
   277  
   278  // ReleaseStatus returns the given release's status.
   279  func (h *Client) ReleaseStatus(rlsName string, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) {
   280  	reqOpts := h.opts
   281  	for _, opt := range opts {
   282  		opt(&reqOpts)
   283  	}
   284  	req := &reqOpts.statusReq
   285  	req.Name = rlsName
   286  	ctx := NewContext()
   287  
   288  	if reqOpts.before != nil {
   289  		if err := reqOpts.before(ctx, req); err != nil {
   290  			return nil, err
   291  		}
   292  	}
   293  	return h.status(ctx, req)
   294  }
   295  
   296  // ReleaseContent returns the configuration for a given release.
   297  func (h *Client) ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) {
   298  	reqOpts := h.opts
   299  	for _, opt := range opts {
   300  		opt(&reqOpts)
   301  	}
   302  	req := &reqOpts.contentReq
   303  	req.Name = rlsName
   304  	ctx := NewContext()
   305  
   306  	if reqOpts.before != nil {
   307  		if err := reqOpts.before(ctx, req); err != nil {
   308  			return nil, err
   309  		}
   310  	}
   311  	return h.content(ctx, req)
   312  }
   313  
   314  // ReleaseHistory returns a release's revision history.
   315  func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error) {
   316  	reqOpts := h.opts
   317  	for _, opt := range opts {
   318  		opt(&reqOpts)
   319  	}
   320  
   321  	req := &reqOpts.histReq
   322  	req.Name = rlsName
   323  	ctx := NewContext()
   324  
   325  	if reqOpts.before != nil {
   326  		if err := reqOpts.before(ctx, req); err != nil {
   327  			return nil, err
   328  		}
   329  	}
   330  	return h.history(ctx, req)
   331  }
   332  
   333  // RunReleaseTest executes a pre-defined test on a release.
   334  func (h *Client) RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) {
   335  	reqOpts := h.opts
   336  	for _, opt := range opts {
   337  		opt(&reqOpts)
   338  	}
   339  
   340  	req := &reqOpts.testReq
   341  	req.Name = rlsName
   342  	ctx := NewContext()
   343  
   344  	return h.test(ctx, req)
   345  }
   346  
   347  // PingTiller pings the Tiller pod and ensures that it is up and running
   348  func (h *Client) PingTiller() error {
   349  	ctx := NewContext()
   350  	return h.ping(ctx)
   351  }
   352  
   353  // connect returns a gRPC connection to Tiller or error. The gRPC dial options
   354  // are constructed here.
   355  func (h *Client) connect(ctx context.Context) (conn *grpc.ClientConn, err error) {
   356  	opts := []grpc.DialOption{
   357  		grpc.WithBlock(),
   358  		grpc.WithKeepaliveParams(keepalive.ClientParameters{
   359  			// Send keepalive every 30 seconds to prevent the connection from
   360  			// getting closed by upstreams
   361  			Time: time.Duration(30) * time.Second,
   362  		}),
   363  		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)),
   364  	}
   365  	switch {
   366  	case h.opts.useTLS:
   367  		opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(h.opts.tlsConfig)))
   368  	default:
   369  		opts = append(opts, grpc.WithInsecure())
   370  	}
   371  	ctx, cancel := context.WithTimeout(ctx, h.opts.connectTimeout)
   372  	defer cancel()
   373  	if conn, err = grpc.DialContext(ctx, h.opts.host, opts...); err != nil {
   374  		return nil, err
   375  	}
   376  	return conn, nil
   377  }
   378  
   379  // list executes tiller.ListReleases RPC.
   380  func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) {
   381  	c, err := h.connect(ctx)
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  	defer c.Close()
   386  
   387  	rlc := rls.NewReleaseServiceClient(c)
   388  	s, err := rlc.ListReleases(ctx, req)
   389  	if err != nil {
   390  		return nil, err
   391  	}
   392  	var resp *rls.ListReleasesResponse
   393  	for {
   394  		r, err := s.Recv()
   395  		if err == io.EOF {
   396  			break
   397  		}
   398  		if err != nil {
   399  			return nil, err
   400  		}
   401  		if resp == nil {
   402  			resp = r
   403  			continue
   404  		}
   405  		resp.Releases = append(resp.Releases, r.GetReleases()...)
   406  	}
   407  	return resp, nil
   408  }
   409  
   410  // install executes tiller.InstallRelease RPC.
   411  func (h *Client) install(ctx context.Context, req *rls.InstallReleaseRequest) (*rls.InstallReleaseResponse, error) {
   412  	c, err := h.connect(ctx)
   413  	if err != nil {
   414  		return nil, err
   415  	}
   416  	defer c.Close()
   417  
   418  	rlc := rls.NewReleaseServiceClient(c)
   419  	return rlc.InstallRelease(ctx, req)
   420  }
   421  
   422  // delete executes tiller.UninstallRelease RPC.
   423  func (h *Client) delete(ctx context.Context, req *rls.UninstallReleaseRequest) (*rls.UninstallReleaseResponse, error) {
   424  	c, err := h.connect(ctx)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  	defer c.Close()
   429  
   430  	rlc := rls.NewReleaseServiceClient(c)
   431  	return rlc.UninstallRelease(ctx, req)
   432  }
   433  
   434  // update executes tiller.UpdateRelease RPC.
   435  func (h *Client) update(ctx context.Context, req *rls.UpdateReleaseRequest) (*rls.UpdateReleaseResponse, error) {
   436  	c, err := h.connect(ctx)
   437  	if err != nil {
   438  		return nil, err
   439  	}
   440  	defer c.Close()
   441  
   442  	rlc := rls.NewReleaseServiceClient(c)
   443  	return rlc.UpdateRelease(ctx, req)
   444  }
   445  
   446  // rollback executes tiller.RollbackRelease RPC.
   447  func (h *Client) rollback(ctx context.Context, req *rls.RollbackReleaseRequest) (*rls.RollbackReleaseResponse, error) {
   448  	c, err := h.connect(ctx)
   449  	if err != nil {
   450  		return nil, err
   451  	}
   452  	defer c.Close()
   453  
   454  	rlc := rls.NewReleaseServiceClient(c)
   455  	return rlc.RollbackRelease(ctx, req)
   456  }
   457  
   458  // status executes tiller.GetReleaseStatus RPC.
   459  func (h *Client) status(ctx context.Context, req *rls.GetReleaseStatusRequest) (*rls.GetReleaseStatusResponse, error) {
   460  	c, err := h.connect(ctx)
   461  	if err != nil {
   462  		return nil, err
   463  	}
   464  	defer c.Close()
   465  
   466  	rlc := rls.NewReleaseServiceClient(c)
   467  	return rlc.GetReleaseStatus(ctx, req)
   468  }
   469  
   470  // content executes tiller.GetReleaseContent RPC.
   471  func (h *Client) content(ctx context.Context, req *rls.GetReleaseContentRequest) (*rls.GetReleaseContentResponse, error) {
   472  	c, err := h.connect(ctx)
   473  	if err != nil {
   474  		return nil, err
   475  	}
   476  	defer c.Close()
   477  
   478  	rlc := rls.NewReleaseServiceClient(c)
   479  	return rlc.GetReleaseContent(ctx, req)
   480  }
   481  
   482  // version executes tiller.GetVersion RPC.
   483  func (h *Client) version(ctx context.Context, req *rls.GetVersionRequest) (*rls.GetVersionResponse, error) {
   484  	c, err := h.connect(ctx)
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  	defer c.Close()
   489  
   490  	rlc := rls.NewReleaseServiceClient(c)
   491  	return rlc.GetVersion(ctx, req)
   492  }
   493  
   494  // history executes tiller.GetHistory RPC.
   495  func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.GetHistoryResponse, error) {
   496  	c, err := h.connect(ctx)
   497  	if err != nil {
   498  		return nil, err
   499  	}
   500  	defer c.Close()
   501  
   502  	rlc := rls.NewReleaseServiceClient(c)
   503  	return rlc.GetHistory(ctx, req)
   504  }
   505  
   506  // test executes tiller.TestRelease RPC.
   507  func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (<-chan *rls.TestReleaseResponse, <-chan error) {
   508  	errc := make(chan error, 1)
   509  	c, err := h.connect(ctx)
   510  	if err != nil {
   511  		errc <- err
   512  		return nil, errc
   513  	}
   514  
   515  	ch := make(chan *rls.TestReleaseResponse, 1)
   516  	go func() {
   517  		defer close(errc)
   518  		defer close(ch)
   519  		defer c.Close()
   520  
   521  		rlc := rls.NewReleaseServiceClient(c)
   522  		s, err := rlc.RunReleaseTest(ctx, req)
   523  		if err != nil {
   524  			errc <- err
   525  			return
   526  		}
   527  
   528  		for {
   529  			msg, err := s.Recv()
   530  			if err == io.EOF {
   531  				return
   532  			}
   533  			if err != nil {
   534  				errc <- err
   535  				return
   536  			}
   537  			ch <- msg
   538  		}
   539  	}()
   540  
   541  	return ch, errc
   542  }
   543  
   544  // ping executes tiller.Ping RPC.
   545  func (h *Client) ping(ctx context.Context) error {
   546  	c, err := h.connect(ctx)
   547  	if err != nil {
   548  		return err
   549  	}
   550  	defer c.Close()
   551  
   552  	healthClient := healthpb.NewHealthClient(c)
   553  	resp, err := healthClient.Check(ctx, &healthpb.HealthCheckRequest{Service: "Tiller"})
   554  	if err != nil {
   555  		return err
   556  	}
   557  	switch resp.GetStatus() {
   558  	case healthpb.HealthCheckResponse_SERVING:
   559  		return nil
   560  	case healthpb.HealthCheckResponse_NOT_SERVING:
   561  		return fmt.Errorf("tiller is not serving requests at this time, Please try again later")
   562  	default:
   563  		return fmt.Errorf("tiller healthcheck returned an unknown status")
   564  	}
   565  }