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