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