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