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 }