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