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