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 }