github.com/wangchanggan/helm@v0.0.0-20211020154240-11b1b7d5406d/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  // 这个函数十分重要,是Client发送请求的函数。
   111  // 该函数会接收一大堆参数,这些参数就是Helm在执行install命令时传入的各种用户指定参数。
   112  func (h *Client) installReleaseFromChartWithContext(ctx context.Context, chart *chart.Chart, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
   113  	// apply the install options
   114  	reqOpts := h.opts
   115  	for _, opt := range opts {
   116  		opt(&reqOpts)
   117  	}
   118  	//将所有安装的参数统一设置到request对象中,构成结构体。
   119  	req := &reqOpts.instReq
   120  	req.Chart = chart
   121  	req.Namespace = ns
   122  	req.DryRun = reqOpts.dryRun
   123  	req.DisableHooks = reqOpts.disableHooks
   124  	req.DisableCrdHook = reqOpts.disableCRDHook
   125  	req.ReuseName = reqOpts.reuseName
   126  	ctx = FromContext(ctx)
   127  
   128  	if reqOpts.before != nil {
   129  		if err := reqOpts.before(ctx, req); err != nil {
   130  			return nil, err
   131  		}
   132  	}
   133  	// 将requirement.yaml中不需要的Chart从安装包结构体中移除。
   134  	err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values)
   135  	if err != nil {
   136  		return nil, err
   137  	}
   138  	// 将父Chart中的value设置给予Chart,这样函数就实现了父Chart向子Chart传递参数。
   139  	err = chartutil.ProcessRequirementsImportValues(req.Chart)
   140  	if err != nil {
   141  		return nil, err
   142  	}
   143  
   144  	// h.install将包装好的req发送给服务端。
   145  	return h.install(ctx, req)
   146  }
   147  
   148  // DeleteRelease uninstalls a named release and returns the response.
   149  func (h *Client) DeleteRelease(rlsName string, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) {
   150  	// apply the uninstall options
   151  	reqOpts := h.opts
   152  	for _, opt := range opts {
   153  		opt(&reqOpts)
   154  	}
   155  
   156  	if reqOpts.dryRun {
   157  		// In the dry run case, just see if the release exists
   158  		r, err := h.ReleaseContent(rlsName)
   159  		if err != nil {
   160  			return &rls.UninstallReleaseResponse{}, err
   161  		}
   162  		return &rls.UninstallReleaseResponse{Release: r.Release}, nil
   163  	}
   164  
   165  	req := &reqOpts.uninstallReq
   166  	req.Name = rlsName
   167  	req.DisableHooks = reqOpts.disableHooks
   168  	ctx := NewContext()
   169  
   170  	if reqOpts.before != nil {
   171  		if err := reqOpts.before(ctx, req); err != nil {
   172  			return nil, err
   173  		}
   174  	}
   175  	return h.delete(ctx, req)
   176  }
   177  
   178  // UpdateRelease loads a chart from chstr and updates a release to a new/different chart.
   179  func (h *Client) UpdateRelease(rlsName string, chstr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   180  	// load the chart to update
   181  	chart, err := chartutil.Load(chstr)
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  
   186  	return h.UpdateReleaseFromChart(rlsName, chart, opts...)
   187  }
   188  
   189  // UpdateReleaseWithContext loads a chart from chstr and updates a release to a new/different chart while accepting a context.
   190  func (h *Client) UpdateReleaseWithContext(ctx context.Context, rlsName string, chstr string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   191  	// load the chart to update
   192  	chart, err := chartutil.Load(chstr)
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	return h.updateReleaseFromChartWithContext(ctx, rlsName, chart, opts...)
   198  }
   199  
   200  // UpdateReleaseFromChart updates a release to a new/different chart.
   201  func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   202  	return h.updateReleaseFromChartWithContext(NewContext(), rlsName, chart, opts...)
   203  }
   204  
   205  // UpdateReleaseFromChartWithContext updates a release to a new/different chart while accepting a context.
   206  func (h *Client) UpdateReleaseFromChartWithContext(ctx context.Context, rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   207  	return h.updateReleaseFromChartWithContext(ctx, rlsName, chart, opts...)
   208  }
   209  
   210  // updateReleaseFromChartWithContext updates a release to a new/different chart and accepts a context.
   211  func (h *Client) updateReleaseFromChartWithContext(ctx context.Context, rlsName string, chart *chart.Chart, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
   212  	// apply the update options
   213  	reqOpts := h.opts
   214  	for _, opt := range opts {
   215  		opt(&reqOpts)
   216  	}
   217  	req := &reqOpts.updateReq
   218  	req.Chart = chart
   219  	req.DryRun = reqOpts.dryRun
   220  	req.Name = rlsName
   221  	req.DisableHooks = reqOpts.disableHooks
   222  	req.Recreate = reqOpts.recreate
   223  	req.Force = reqOpts.force
   224  	req.ResetValues = reqOpts.resetValues
   225  	req.ReuseValues = reqOpts.reuseValues
   226  	ctx = FromContext(ctx)
   227  
   228  	if reqOpts.before != nil {
   229  		if err := reqOpts.before(ctx, req); err != nil {
   230  			return nil, err
   231  		}
   232  	}
   233  	err := chartutil.ProcessRequirementsEnabled(req.Chart, req.Values)
   234  	if err != nil {
   235  		return nil, err
   236  	}
   237  	err = chartutil.ProcessRequirementsImportValues(req.Chart)
   238  	if err != nil {
   239  		return nil, err
   240  	}
   241  
   242  	return h.update(ctx, req)
   243  }
   244  
   245  // GetVersion returns the server version.
   246  func (h *Client) GetVersion(opts ...VersionOption) (*rls.GetVersionResponse, error) {
   247  	reqOpts := h.opts
   248  	for _, opt := range opts {
   249  		opt(&reqOpts)
   250  	}
   251  	req := &rls.GetVersionRequest{}
   252  	ctx := NewContext()
   253  
   254  	if reqOpts.before != nil {
   255  		if err := reqOpts.before(ctx, req); err != nil {
   256  			return nil, err
   257  		}
   258  	}
   259  	return h.version(ctx, req)
   260  }
   261  
   262  // RollbackRelease rolls back a release to the previous version.
   263  func (h *Client) RollbackRelease(rlsName string, opts ...RollbackOption) (*rls.RollbackReleaseResponse, error) {
   264  	reqOpts := h.opts
   265  	for _, opt := range opts {
   266  		opt(&reqOpts)
   267  	}
   268  	req := &reqOpts.rollbackReq
   269  	req.Recreate = reqOpts.recreate
   270  	req.Force = reqOpts.force
   271  	req.DisableHooks = reqOpts.disableHooks
   272  	req.DryRun = reqOpts.dryRun
   273  	req.Name = rlsName
   274  	ctx := NewContext()
   275  
   276  	if reqOpts.before != nil {
   277  		if err := reqOpts.before(ctx, req); err != nil {
   278  			return nil, err
   279  		}
   280  	}
   281  	return h.rollback(ctx, req)
   282  }
   283  
   284  // ReleaseStatus returns the given release's status.
   285  func (h *Client) ReleaseStatus(rlsName string, opts ...StatusOption) (*rls.GetReleaseStatusResponse, error) {
   286  	reqOpts := h.opts
   287  	for _, opt := range opts {
   288  		opt(&reqOpts)
   289  	}
   290  	req := &reqOpts.statusReq
   291  	req.Name = rlsName
   292  	ctx := NewContext()
   293  
   294  	if reqOpts.before != nil {
   295  		if err := reqOpts.before(ctx, req); err != nil {
   296  			return nil, err
   297  		}
   298  	}
   299  	return h.status(ctx, req)
   300  }
   301  
   302  // ReleaseContent returns the configuration for a given release.
   303  func (h *Client) ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) {
   304  	reqOpts := h.opts
   305  	for _, opt := range opts {
   306  		opt(&reqOpts)
   307  	}
   308  	req := &reqOpts.contentReq
   309  	req.Name = rlsName
   310  	ctx := NewContext()
   311  
   312  	if reqOpts.before != nil {
   313  		if err := reqOpts.before(ctx, req); err != nil {
   314  			return nil, err
   315  		}
   316  	}
   317  	return h.content(ctx, req)
   318  }
   319  
   320  // ReleaseHistory returns a release's revision history.
   321  func (h *Client) ReleaseHistory(rlsName string, opts ...HistoryOption) (*rls.GetHistoryResponse, error) {
   322  	reqOpts := h.opts
   323  	for _, opt := range opts {
   324  		opt(&reqOpts)
   325  	}
   326  
   327  	req := &reqOpts.histReq
   328  	req.Name = rlsName
   329  	ctx := NewContext()
   330  
   331  	if reqOpts.before != nil {
   332  		if err := reqOpts.before(ctx, req); err != nil {
   333  			return nil, err
   334  		}
   335  	}
   336  	return h.history(ctx, req)
   337  }
   338  
   339  // RunReleaseTest executes a pre-defined test on a release.
   340  func (h *Client) RunReleaseTest(rlsName string, opts ...ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) {
   341  	reqOpts := h.opts
   342  	for _, opt := range opts {
   343  		opt(&reqOpts)
   344  	}
   345  
   346  	req := &reqOpts.testReq
   347  	req.Name = rlsName
   348  	ctx := NewContext()
   349  
   350  	return h.test(ctx, req)
   351  }
   352  
   353  // PingTiller pings the Tiller pod and ensures that it is up and running
   354  func (h *Client) PingTiller() error {
   355  	ctx := NewContext()
   356  	return h.ping(ctx)
   357  }
   358  
   359  // connect returns a gRPC connection to Tiller or error. The gRPC dial options
   360  // are constructed here.
   361  func (h *Client) connect(ctx context.Context) (conn *grpc.ClientConn, err error) {
   362  	// 设置grpc的参数,grpc.DialOption这里主要是默认30s超时时间,设置最大消息大小,默认20MB。
   363  	opts := []grpc.DialOption{
   364  		grpc.WithBlock(),
   365  		grpc.WithKeepaliveParams(keepalive.ClientParameters{
   366  			// Send keepalive every 30 seconds to prevent the connection from
   367  			// getting closed by upstreams
   368  			Time: time.Duration(30) * time.Second,
   369  		}),
   370  		grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(maxMsgSize)),
   371  	}
   372  	// 根据是否启用tls选择对应的证书信息。
   373  	switch {
   374  	case h.opts.useTLS:
   375  		opts = append(opts, grpc.WithTransportCredentials(credentials.NewTLS(h.opts.tlsConfig)))
   376  	default:
   377  		opts = append(opts, grpc.WithInsecure())
   378  	}
   379  	ctx, cancel := context.WithTimeout(ctx, h.opts.connectTimeout)
   380  	defer cancel()
   381  	// grpc.DialContext(ctx, h.opts.host, opts...)非常重要,port-forward会建立一个本地和远程之间的连接
   382  	// h.opts.host就是本地的连接端口,也就是说,建立与这个地址的连接,发送的数据就会直接送达远端的Tiller Pod。
   383  	if conn, err = grpc.DialContext(ctx, h.opts.host, opts...); err != nil {
   384  		return nil, err
   385  	}
   386  	return conn, nil
   387  }
   388  
   389  // list executes tiller.ListReleases RPC.
   390  func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) {
   391  	// 首先创建连接Tiller的grpc客户端。
   392  	c, err := h.connect(ctx)
   393  	if err != nil {
   394  		return nil, err
   395  	}
   396  	defer c.Close()
   397  
   398  	rlc := rls.NewReleaseServiceClient(c)
   399  	// 请求Tiller的rlc.ListReleases接口。
   400  	s, err := rlc.ListReleases(ctx, req)
   401  	if err != nil {
   402  		return nil, err
   403  	}
   404  	var resp *rls.ListReleasesResponse
   405  	// 循环接受Tiller返回的Release。
   406  	for {
   407  		r, err := s.Recv()
   408  		if err == io.EOF {
   409  			break
   410  		}
   411  		if err != nil {
   412  			return nil, err
   413  		}
   414  		if resp == nil {
   415  			resp = r
   416  			continue
   417  		}
   418  		// 由于Release列表比较多,所以将每次获取的Release拼接成数组最终返回。
   419  		resp.Releases = append(resp.Releases, r.GetReleases()...)
   420  	}
   421  	return resp, nil
   422  }
   423  
   424  // install executes tiller.InstallRelease RPC.
   425  // 直接将拼装好的信息调用grpc接口发送给Tiller
   426  func (h *Client) install(ctx context.Context, req *rls.InstallReleaseRequest) (*rls.InstallReleaseResponse, error) {
   427  	// 首先建立一个grpc连接
   428  	c, err := h.connect(ctx)
   429  	if err != nil {
   430  		return nil, err
   431  	}
   432  	defer c.Close()
   433  
   434  	rlc := rls.NewReleaseServiceClient(c)
   435  	return rlc.InstallRelease(ctx, req)
   436  }
   437  
   438  // delete executes tiller.UninstallRelease RPC.
   439  func (h *Client) delete(ctx context.Context, req *rls.UninstallReleaseRequest) (*rls.UninstallReleaseResponse, error) {
   440  	c, err := h.connect(ctx)
   441  	if err != nil {
   442  		return nil, err
   443  	}
   444  	defer c.Close()
   445  
   446  	rlc := rls.NewReleaseServiceClient(c)
   447  	return rlc.UninstallRelease(ctx, req)
   448  }
   449  
   450  // update executes tiller.UpdateRelease RPC.
   451  func (h *Client) update(ctx context.Context, req *rls.UpdateReleaseRequest) (*rls.UpdateReleaseResponse, error) {
   452  	c, err := h.connect(ctx)
   453  	if err != nil {
   454  		return nil, err
   455  	}
   456  	defer c.Close()
   457  
   458  	rlc := rls.NewReleaseServiceClient(c)
   459  	return rlc.UpdateRelease(ctx, req)
   460  }
   461  
   462  // rollback executes tiller.RollbackRelease RPC.
   463  func (h *Client) rollback(ctx context.Context, req *rls.RollbackReleaseRequest) (*rls.RollbackReleaseResponse, error) {
   464  	c, err := h.connect(ctx)
   465  	if err != nil {
   466  		return nil, err
   467  	}
   468  	defer c.Close()
   469  
   470  	rlc := rls.NewReleaseServiceClient(c)
   471  	return rlc.RollbackRelease(ctx, req)
   472  }
   473  
   474  // status executes tiller.GetReleaseStatus RPC.
   475  func (h *Client) status(ctx context.Context, req *rls.GetReleaseStatusRequest) (*rls.GetReleaseStatusResponse, error) {
   476  	c, err := h.connect(ctx)
   477  	if err != nil {
   478  		return nil, err
   479  	}
   480  	defer c.Close()
   481  
   482  	rlc := rls.NewReleaseServiceClient(c)
   483  	return rlc.GetReleaseStatus(ctx, req)
   484  }
   485  
   486  // content executes tiller.GetReleaseContent RPC.
   487  func (h *Client) content(ctx context.Context, req *rls.GetReleaseContentRequest) (*rls.GetReleaseContentResponse, error) {
   488  	c, err := h.connect(ctx)
   489  	if err != nil {
   490  		return nil, err
   491  	}
   492  	defer c.Close()
   493  
   494  	rlc := rls.NewReleaseServiceClient(c)
   495  	return rlc.GetReleaseContent(ctx, req)
   496  }
   497  
   498  // version executes tiller.GetVersion RPC.
   499  func (h *Client) version(ctx context.Context, req *rls.GetVersionRequest) (*rls.GetVersionResponse, error) {
   500  	c, err := h.connect(ctx)
   501  	if err != nil {
   502  		return nil, err
   503  	}
   504  	defer c.Close()
   505  
   506  	rlc := rls.NewReleaseServiceClient(c)
   507  	return rlc.GetVersion(ctx, req)
   508  }
   509  
   510  // history executes tiller.GetHistory RPC.
   511  func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.GetHistoryResponse, error) {
   512  	c, err := h.connect(ctx)
   513  	if err != nil {
   514  		return nil, err
   515  	}
   516  	defer c.Close()
   517  
   518  	rlc := rls.NewReleaseServiceClient(c)
   519  	return rlc.GetHistory(ctx, req)
   520  }
   521  
   522  // test executes tiller.TestRelease RPC.
   523  func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (<-chan *rls.TestReleaseResponse, <-chan error) {
   524  	errc := make(chan error, 1)
   525  	c, err := h.connect(ctx)
   526  	if err != nil {
   527  		errc <- err
   528  		return nil, errc
   529  	}
   530  
   531  	ch := make(chan *rls.TestReleaseResponse, 1)
   532  	go func() {
   533  		defer close(errc)
   534  		defer close(ch)
   535  		defer c.Close()
   536  
   537  		rlc := rls.NewReleaseServiceClient(c)
   538  		s, err := rlc.RunReleaseTest(ctx, req)
   539  		if err != nil {
   540  			errc <- err
   541  			return
   542  		}
   543  
   544  		for {
   545  			msg, err := s.Recv()
   546  			if err == io.EOF {
   547  				return
   548  			}
   549  			if err != nil {
   550  				errc <- err
   551  				return
   552  			}
   553  			ch <- msg
   554  		}
   555  	}()
   556  
   557  	return ch, errc
   558  }
   559  
   560  // ping executes tiller.Ping RPC.
   561  func (h *Client) ping(ctx context.Context) error {
   562  	c, err := h.connect(ctx)
   563  	if err != nil {
   564  		return err
   565  	}
   566  	defer c.Close()
   567  
   568  	healthClient := healthpb.NewHealthClient(c)
   569  	resp, err := healthClient.Check(ctx, &healthpb.HealthCheckRequest{Service: "Tiller"})
   570  	if err != nil {
   571  		return err
   572  	}
   573  	switch resp.GetStatus() {
   574  	case healthpb.HealthCheckResponse_SERVING:
   575  		return nil
   576  	case healthpb.HealthCheckResponse_NOT_SERVING:
   577  		return fmt.Errorf("tiller is not serving requests at this time, Please try again later")
   578  	default:
   579  		return fmt.Errorf("tiller healthcheck returned an unknown status")
   580  	}
   581  }