github.com/azlyth/helm@v2.8.2+incompatible/pkg/helm/client.go (about)

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