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