Add validate to Install / Upgrade commands that use kubernets to validate the manifest before reifying

pull/1217/head
Ville Aikas 9 years ago
parent da768867a3
commit a5c8b9c401

@ -188,6 +188,10 @@ message UpdateReleaseRequest {
bool dry_run = 4; bool dry_run = 4;
// DisableHooks causes the server to skip running any hooks for the upgrade. // DisableHooks causes the server to skip running any hooks for the upgrade.
bool disable_hooks = 5; bool disable_hooks = 5;
// Validate requests that Tiller uses kubernetes validation logic on the
// manifest before handing it off for reification
bool validate = 6;
} }
// UpdateReleaseResponse is the response to an update request. // UpdateReleaseResponse is the response to an update request.
@ -235,6 +239,10 @@ message InstallReleaseRequest {
// ReuseName requests that Tiller re-uses a name, instead of erroring out. // ReuseName requests that Tiller re-uses a name, instead of erroring out.
bool reuse_name = 7; bool reuse_name = 7;
// Validate requests that Tiller uses kubernetes validation logic on the
// manifest before handing it off for reification
bool validate = 8;
} }
// InstallReleaseResponse is the response from a release installation. // InstallReleaseResponse is the response from a release installation.

@ -90,6 +90,7 @@ type installCmd struct {
disableHooks bool disableHooks bool
replace bool replace bool
verify bool verify bool
validate bool
keyring string keyring string
out io.Writer out io.Writer
client helm.Interface client helm.Interface
@ -135,6 +136,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2")
f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release")
f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it") f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it")
f.BoolVar(&inst.validate, "validate", false, "validate manifest before reifying it")
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification") f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed.") f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed.")
return cmd return cmd
@ -167,6 +169,7 @@ func (i *installCmd) run() error {
helm.ReleaseName(i.name), helm.ReleaseName(i.name),
helm.InstallDryRun(i.dryRun), helm.InstallDryRun(i.dryRun),
helm.InstallReuseName(i.replace), helm.InstallReuseName(i.replace),
helm.InstallValidate(i.validate),
helm.InstallDisableHooks(i.disableHooks)) helm.InstallDisableHooks(i.disableHooks))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)

@ -51,6 +51,7 @@ type upgradeCmd struct {
valuesFile string valuesFile string
values *values values *values
verify bool verify bool
validate bool
keyring string keyring string
install bool install bool
namespace string namespace string
@ -89,6 +90,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.Var(upgrade.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") f.Var(upgrade.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2")
f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks") f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks")
f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading") f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading")
f.BoolVar(&upgrade.validate, "validate", false, "validate manifest before reifying it")
f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "the path to the keyring that contains public singing keys") f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "the path to the keyring that contains public singing keys")
f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install") f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
f.StringVar(&upgrade.namespace, "namespace", "default", "the namespace to install the release into (only used if --install is set)") f.StringVar(&upgrade.namespace, "namespace", "default", "the namespace to install the release into (only used if --install is set)")
@ -135,7 +137,7 @@ func (u *upgradeCmd) run() error {
return err return err
} }
_, err = u.client.UpdateRelease(u.release, chartPath, helm.UpdateValueOverrides(rawVals), helm.UpgradeDryRun(u.dryRun), helm.UpgradeDisableHooks(u.disableHooks)) _, err = u.client.UpdateRelease(u.release, chartPath, helm.UpdateValueOverrides(rawVals), helm.UpgradeDryRun(u.dryRun), helm.UpgradeDisableHooks(u.disableHooks), helm.UpgradeValidate(u.validate))
if err != nil { if err != nil {
return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err))
} }

@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"log" "log"
"os"
"regexp" "regexp"
"strings" "strings"
@ -39,6 +40,9 @@ import (
"k8s.io/helm/pkg/storage/driver" "k8s.io/helm/pkg/storage/driver"
"k8s.io/helm/pkg/timeconv" "k8s.io/helm/pkg/timeconv"
"k8s.io/helm/pkg/version" "k8s.io/helm/pkg/version"
"k8s.io/kubernetes/pkg/api/unversioned"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
) )
var srv *releaseServer var srv *releaseServer
@ -305,6 +309,19 @@ func (s *releaseServer) performUpdate(originalRelease, updatedRelease *release.R
} }
} }
kubeCli := s.env.KubeClient
original := bytes.NewBufferString(originalRelease.Manifest)
modified := bytes.NewBufferString(updatedRelease.Manifest)
// Validate the manifest
if req.Validate {
log.Printf("Validating manifest: %s\n", modified)
err := validateResources(updatedRelease.Namespace, modified)
if err != nil {
return nil, err
}
}
if err := s.performKubeUpdate(originalRelease, updatedRelease); err != nil { if err := s.performKubeUpdate(originalRelease, updatedRelease); err != nil {
log.Printf("warning: Release Upgrade %q failed: %s", updatedRelease.Name, err) log.Printf("warning: Release Upgrade %q failed: %s", updatedRelease.Name, err)
originalRelease.Info.Status.Code = release.Status_SUPERSEDED originalRelease.Info.Status.Code = release.Status_SUPERSEDED
@ -736,6 +753,16 @@ func (s *releaseServer) performRelease(r *release.Release, req *services.Install
// regular manifests // regular manifests
kubeCli := s.env.KubeClient kubeCli := s.env.KubeClient
b := bytes.NewBufferString(r.Manifest) b := bytes.NewBufferString(r.Manifest)
// Validate the manifest
if req.Validate {
log.Printf("Validating manifest: %s\n", b)
err := validateResources(r.Namespace, b)
if err != nil {
return res, err
}
}
if err := kubeCli.Create(r.Namespace, b); err != nil { if err := kubeCli.Create(r.Namespace, b); err != nil {
log.Printf("warning: Release %q failed: %s", r.Name, err) log.Printf("warning: Release %q failed: %s", r.Name, err)
r.Info.Status.Code = release.Status_FAILED r.Info.Status.Code = release.Status_FAILED
@ -885,8 +912,40 @@ func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
if len(es) > 0 { if len(es) > 0 {
errs = fmt.Errorf("deletion error count %d: %s", len(es), strings.Join(es, "; ")) errs = fmt.Errorf("deletion error count %d: %s", len(es), strings.Join(es, "; "))
} }
return res, nil
}
return res, errs // validateResources takes a namespace and a manifest (fully expanded set of templates) and
// validates resources.
func validateResources(namespace string, manifest *bytes.Buffer) error {
f := cmdutil.NewFactory(nil)
schema, err := f.Validator(true, os.TempDir())
if err != nil {
return err
}
mapper, typer := f.Object(true)
r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
Schema(schema).
NamespaceParam(namespace).DefaultNamespace().
Stream(manifest, "release").
Flatten().
Do()
err = r.Err()
if err != nil {
return err
}
err = r.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
return nil
})
if err != nil {
return err
}
return nil
} }
func splitManifests(bigfile string) map[string]string { func splitManifests(bigfile string) map[string]string {

@ -17,6 +17,7 @@ limitations under the License.
package main package main
import ( import (
"bytes"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -70,6 +71,23 @@ data:
name: value name: value
` `
var invalidResource = `apiVersion: v1
kind: ConfigMap
metadata:
naaame: test-cm
data:
name: value
`
var validResource = `apiVersion: v1
kind: ConfigMap
metadata:
name: test-cm
data:
name: value
`
func rsFixture() *releaseServer { func rsFixture() *releaseServer {
return &releaseServer{ return &releaseServer{
env: mockEnvironment(), env: mockEnvironment(),
@ -511,6 +529,148 @@ func TestInstallReleaseReuseName(t *testing.T) {
} }
} }
func TestInstallReleaseWithValidationFailsForInvalid(t *testing.T) {
c := context.Background()
rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{
Namespace: "spaced",
Validate: true,
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "invalid", Data: []byte(invalidResource)},
},
},
}
_, err := rs.InstallRelease(c, req)
if err == nil {
t.Fatalf("Install succeeded but was expected to fail")
}
if !strings.Contains(err.Error(), "naaame") {
t.Errorf("didn't fail with expected error")
}
}
func TestInstallReleaseWithValidationWorksForValid(t *testing.T) {
c := context.Background()
rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{
Namespace: "spaced",
Validate: true,
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "invalid", Data: []byte(validResource)},
},
},
}
res, err := rs.InstallRelease(c, req)
if err != nil {
t.Fatalf("Failed install: %s %s", err, req.Chart)
}
if res.Release.Name == "" {
t.Errorf("Expected release name.")
}
if res.Release.Namespace != "spaced" {
t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace)
}
rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version)
if err != nil {
t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases)
}
if len(rel.Manifest) == 0 {
t.Errorf("Expected manifest in %v", res)
}
}
func TestUpdateReleaseWithValidationFailsForInvalid(t *testing.T) {
c := context.Background()
rs := rsFixture()
req := &services.InstallReleaseRequest{
Name: "myspecialone",
Namespace: "spaced",
Validate: true,
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "invalid", Data: []byte(validResource)},
},
},
}
_, err := rs.InstallRelease(c, req)
if err != nil {
t.Fatalf("Install failed: %v", err)
}
uReq := &services.UpdateReleaseRequest{
Name: "myspecialone",
Validate: true,
DisableHooks: true,
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "invalid", Data: []byte(invalidResource)},
},
},
}
_, err = rs.UpdateRelease(c, uReq)
if err == nil {
t.Fatalf("Install succeeded but was expected to fail")
}
if !strings.Contains(err.Error(), "naaame") {
t.Errorf("didn't fail with expected error")
}
}
func TestUpdateReleaseWithValidationWorksForValid(t *testing.T) {
c := context.Background()
rs := rsFixture()
req := &services.InstallReleaseRequest{
Name: "myspecialone",
Namespace: "spaced",
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "invalid", Data: []byte(invalidResource)},
},
},
}
_, err := rs.InstallRelease(c, req)
if err != nil {
t.Fatalf("Install failed: %v", err)
}
uReq := &services.UpdateReleaseRequest{
Name: "myspecialone",
Validate: true,
DisableHooks: true,
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "invalid", Data: []byte(validResource)},
},
},
}
_, err = rs.UpdateRelease(c, uReq)
if err != nil {
t.Fatalf("Install failed: %v", err)
}
}
func TestUpdateRelease(t *testing.T) { func TestUpdateRelease(t *testing.T) {
c := helm.NewContext() c := helm.NewContext()
rs := rsFixture() rs := rsFixture()
@ -1198,6 +1358,11 @@ func TestListReleasesFilter(t *testing.T) {
} }
} }
func testValidateResources(t *testing.T) {
b := bytes.NewBufferString("")
validateResources("default", b)
}
func mockEnvironment() *environment.Environment { func mockEnvironment() *environment.Environment {
e := environment.New() e := environment.New()
e.Releases = storage.Init(driver.NewMemory()) e.Releases = storage.Init(driver.NewMemory())

@ -38,6 +38,8 @@ type options struct {
host string host string
// if set dry-run helm client calls // if set dry-run helm client calls
dryRun bool dryRun bool
// if set validate manifest
validate bool
// if set, re-use an existing name // if set, re-use an existing name
reuseName bool reuseName bool
// if set, skip running hooks // if set, skip running hooks
@ -219,6 +221,22 @@ func RollbackVersion(ver int32) RollbackOption {
} }
} }
// UpgradeValidate will (if true) instruct Tiller to validate manifest before
// reifying
func UpgradeValidate(validate bool) UpdateOption {
return func(opts *options) {
opts.validate = validate
}
}
// InstallValidate will (if true) instruct Tiller to validate manifest before
// reifying
func InstallValidate(validate bool) InstallOption {
return func(opts *options) {
opts.validate = validate
}
}
// UpgradeDisableHooks will disable hooks for an upgrade operation. // UpgradeDisableHooks will disable hooks for an upgrade operation.
func UpgradeDisableHooks(disable bool) UpdateOption { func UpgradeDisableHooks(disable bool) UpdateOption {
return func(opts *options) { return func(opts *options) {
@ -278,6 +296,75 @@ type RollbackOption func(*options)
// issuing a GetHistory rpc. // issuing a GetHistory rpc.
type HistoryOption func(*options) type HistoryOption func(*options)
// RPC helpers defined on `options` type. Note: These actually execute the
// the corresponding tiller RPC. There is no particular reason why these
// are APIs are hung off `options`, they are internal to pkg/helm to remain
// malleable.
// Executes tiller.ListReleases RPC.
func (o *options) rpcListReleases(rlc rls.ReleaseServiceClient, opts ...ReleaseListOption) (*rls.ListReleasesResponse, error) {
// apply release list options
for _, opt := range opts {
opt(o)
}
s, err := rlc.ListReleases(context.TODO(), &o.listReq)
if err != nil {
return nil, err
}
return s.Recv()
}
// Executes tiller.InstallRelease RPC.
func (o *options) rpcInstallRelease(chr *cpb.Chart, rlc rls.ReleaseServiceClient, ns string, opts ...InstallOption) (*rls.InstallReleaseResponse, error) {
// apply the install options
for _, opt := range opts {
opt(o)
}
o.instReq.Chart = chr
o.instReq.Namespace = ns
o.instReq.DryRun = o.dryRun
o.instReq.DisableHooks = o.disableHooks
o.instReq.ReuseName = o.reuseName
o.instReq.Validate = o.validate
return rlc.InstallRelease(context.TODO(), &o.instReq)
}
// Executes tiller.UninstallRelease RPC.
func (o *options) rpcDeleteRelease(rlsName string, rlc rls.ReleaseServiceClient, opts ...DeleteOption) (*rls.UninstallReleaseResponse, error) {
for _, opt := range opts {
opt(o)
}
if o.dryRun {
// In the dry run case, just see if the release exists
r, err := o.rpcGetReleaseContent(rlsName, rlc)
if err != nil {
return &rls.UninstallReleaseResponse{}, err
}
return &rls.UninstallReleaseResponse{Release: r.Release}, nil
}
o.uninstallReq.Name = rlsName
o.uninstallReq.DisableHooks = o.disableHooks
return rlc.UninstallRelease(context.TODO(), &o.uninstallReq)
}
// Executes tiller.UpdateRelease RPC.
func (o *options) rpcUpdateRelease(rlsName string, chr *cpb.Chart, rlc rls.ReleaseServiceClient, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) {
for _, opt := range opts {
opt(o)
}
o.updateReq.Chart = chr
o.updateReq.DryRun = o.dryRun
o.updateReq.Name = rlsName
o.updateReq.Validate = o.validate
return rlc.UpdateRelease(context.TODO(), &o.updateReq)
}
// WithMaxHistory sets the max number of releases to return // WithMaxHistory sets the max number of releases to return
// in a release history query. // in a release history query.
func WithMaxHistory(max int32) HistoryOption { func WithMaxHistory(max int32) HistoryOption {

@ -246,6 +246,9 @@ type UpdateReleaseRequest struct {
DryRun bool `protobuf:"varint,4,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"` DryRun bool `protobuf:"varint,4,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"`
// DisableHooks causes the server to skip running any hooks for the upgrade. // DisableHooks causes the server to skip running any hooks for the upgrade.
DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"` DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
// Validate requests that Tiller uses kubernetes validation logic on the
// manifest before handing it off for reification
Validate bool `protobuf:"varint,6,opt,name=validate" json:"validate,omitempty"`
} }
func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} }
@ -337,6 +340,9 @@ type InstallReleaseRequest struct {
Namespace string `protobuf:"bytes,6,opt,name=namespace" json:"namespace,omitempty"` Namespace string `protobuf:"bytes,6,opt,name=namespace" json:"namespace,omitempty"`
// ReuseName requests that Tiller re-uses a name, instead of erroring out. // ReuseName requests that Tiller re-uses a name, instead of erroring out.
ReuseName bool `protobuf:"varint,7,opt,name=reuse_name,json=reuseName" json:"reuse_name,omitempty"` ReuseName bool `protobuf:"varint,7,opt,name=reuse_name,json=reuseName" json:"reuse_name,omitempty"`
// Validate requests that Tiller uses kubernetes validation logic on the
// manifest before handing it off for reification
Validate bool `protobuf:"varint,8,opt,name=validate" json:"validate,omitempty"`
} }
func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} } func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} }
@ -878,6 +884,7 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) } func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
<<<<<<< 70b29a47d08628a6909964bcfc35f4a60507ea30
// 1004 bytes of a gzipped FileDescriptorProto // 1004 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdf, 0x6f, 0xe3, 0xc4, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdf, 0x6f, 0xe3, 0xc4,
0x13, 0xaf, 0x93, 0x34, 0x3f, 0xa6, 0x3f, 0xbe, 0xe9, 0x5e, 0xda, 0xb8, 0xd6, 0x17, 0x14, 0x19, 0x13, 0xaf, 0x93, 0x34, 0x3f, 0xa6, 0x3f, 0xbe, 0xe9, 0x5e, 0xda, 0xb8, 0xd6, 0x17, 0x14, 0x19,
@ -942,4 +949,64 @@ var fileDescriptor0 = []byte{
0xe7, 0x39, 0xb5, 0x13, 0x41, 0xa9, 0xc2, 0xde, 0x12, 0x54, 0xbc, 0x6b, 0x6c, 0x09, 0x2a, 0xd1, 0xe7, 0x39, 0xb5, 0x13, 0x41, 0xa9, 0xc2, 0xde, 0x12, 0x54, 0xbc, 0x6b, 0x6c, 0x09, 0x2a, 0xd1,
0x23, 0xec, 0xbd, 0x17, 0xf0, 0x73, 0x55, 0xeb, 0xdd, 0x95, 0xc5, 0x5f, 0x85, 0xaf, 0xfe, 0x0d, 0x23, 0xec, 0xbd, 0x17, 0xf0, 0x73, 0x55, 0xeb, 0xdd, 0x95, 0xc5, 0x5f, 0x85, 0xaf, 0xfe, 0x0d,
0x00, 0x00, 0xff, 0xff, 0x9a, 0xb0, 0xe9, 0x29, 0xfb, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9a, 0xb0, 0xe9, 0x29, 0xfb, 0x0c, 0x00, 0x00,
=======
// 909 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdd, 0x6e, 0xe3, 0x44,
0x14, 0xde, 0xfc, 0xd4, 0x71, 0x4e, 0x7f, 0xd4, 0xce, 0xf6, 0xc7, 0xb5, 0x00, 0xad, 0x8c, 0x60,
0xcb, 0x02, 0x29, 0x84, 0x5b, 0x84, 0xd4, 0xcd, 0x46, 0xdd, 0x6a, 0x43, 0x56, 0x9a, 0x50, 0x90,
0xb8, 0x20, 0x72, 0x93, 0xc9, 0xc6, 0xe0, 0xda, 0xc1, 0x33, 0x89, 0xe8, 0x23, 0xf0, 0x04, 0x5c,
0x70, 0xc7, 0x5b, 0xf1, 0x36, 0xcc, 0xaf, 0x1b, 0xbb, 0x36, 0xf5, 0xe6, 0x26, 0xf6, 0xcc, 0xf9,
0xe6, 0x3b, 0xe7, 0x7c, 0x67, 0xce, 0x71, 0xc0, 0x9d, 0xfb, 0x8b, 0xe0, 0x9c, 0x92, 0x64, 0x15,
0x4c, 0x08, 0x3d, 0x67, 0x41, 0x18, 0x92, 0xa4, 0xb3, 0x48, 0x62, 0x16, 0xa3, 0x43, 0x61, 0xeb,
0x18, 0x5b, 0x47, 0xd9, 0xdc, 0x63, 0x79, 0x62, 0x32, 0xf7, 0x13, 0xa6, 0x7e, 0x15, 0xda, 0x3d,
0x59, 0xdf, 0x8f, 0xa3, 0x59, 0xf0, 0x4e, 0x1b, 0x94, 0x8b, 0x84, 0x84, 0xc4, 0xa7, 0xc4, 0x3c,
0x33, 0x87, 0x8c, 0x2d, 0x88, 0x66, 0xb1, 0x36, 0x9c, 0x66, 0x0c, 0x94, 0xf9, 0x6c, 0x49, 0x33,
0x7c, 0x2b, 0x92, 0xd0, 0x20, 0x8e, 0xcc, 0x53, 0xd9, 0xbc, 0x7f, 0xea, 0xf0, 0x74, 0x10, 0x50,
0x86, 0xd5, 0x41, 0x8a, 0xc9, 0xef, 0x4b, 0x42, 0x19, 0x3a, 0x84, 0xad, 0x30, 0xb8, 0x0d, 0x98,
0x53, 0x7b, 0x56, 0x3b, 0x6b, 0x60, 0xb5, 0x40, 0xc7, 0x60, 0xc5, 0xb3, 0x19, 0x25, 0xcc, 0xa9,
0xf3, 0xed, 0x36, 0xd6, 0x2b, 0xf4, 0x1d, 0xb4, 0x68, 0x9c, 0xb0, 0xf1, 0xcd, 0x9d, 0xd3, 0xe0,
0x86, 0xbd, 0xee, 0x27, 0x9d, 0x22, 0x29, 0x3a, 0xc2, 0xd3, 0x88, 0x03, 0x3b, 0xe2, 0xe7, 0xe5,
0x1d, 0xb6, 0xa8, 0x7c, 0x0a, 0xde, 0x59, 0x10, 0x32, 0x92, 0x38, 0x4d, 0xc5, 0xab, 0x56, 0xe8,
0x12, 0x40, 0xf2, 0xc6, 0xc9, 0x94, 0xdb, 0xb6, 0x24, 0xf5, 0x59, 0x05, 0xea, 0xb7, 0x02, 0x8f,
0xdb, 0xd4, 0xbc, 0xa2, 0x6f, 0x61, 0x47, 0x49, 0x32, 0x9e, 0xc4, 0x53, 0x42, 0x1d, 0xeb, 0x59,
0x83, 0x53, 0x9d, 0x2a, 0x2a, 0xa3, 0xf0, 0x48, 0x89, 0xd6, 0xe3, 0x08, 0xbc, 0xad, 0xe0, 0xe2,
0x9d, 0x7a, 0xbf, 0x80, 0x6d, 0xe8, 0xbd, 0x2e, 0x58, 0x2a, 0x78, 0xb4, 0x0d, 0xad, 0xeb, 0xe1,
0x9b, 0xe1, 0xdb, 0x9f, 0x86, 0xfb, 0x4f, 0x90, 0x0d, 0xcd, 0xe1, 0xc5, 0xf7, 0xfd, 0xfd, 0x1a,
0x3a, 0x80, 0xdd, 0xc1, 0xc5, 0xe8, 0x87, 0x31, 0xee, 0x0f, 0xfa, 0x17, 0xa3, 0xfe, 0xab, 0xfd,
0xba, 0xf7, 0x11, 0xb4, 0xd3, 0xa8, 0x50, 0x0b, 0x1a, 0x17, 0xa3, 0x9e, 0x3a, 0xf2, 0xaa, 0xcf,
0xdf, 0x6a, 0xde, 0x9f, 0x35, 0x38, 0xcc, 0x16, 0x81, 0x2e, 0xe2, 0x88, 0x12, 0x51, 0x85, 0x49,
0xbc, 0x8c, 0xd2, 0x2a, 0xc8, 0x05, 0x42, 0xd0, 0x8c, 0xc8, 0x1f, 0xa6, 0x06, 0xf2, 0x5d, 0x20,
0x59, 0xcc, 0xfc, 0x50, 0xea, 0xcf, 0x91, 0x72, 0x81, 0xbe, 0x06, 0x5b, 0x27, 0x47, 0xb9, 0xb2,
0x8d, 0xb3, 0xed, 0xee, 0x51, 0x36, 0x65, 0xed, 0x11, 0xa7, 0x30, 0xef, 0x12, 0x4e, 0x2e, 0x89,
0x89, 0x44, 0x29, 0x62, 0xee, 0x84, 0xf0, 0xeb, 0xdf, 0x12, 0x19, 0x8c, 0xf0, 0xcb, 0xdf, 0x91,
0x03, 0x2d, 0x7d, 0xa1, 0x64, 0x38, 0x5b, 0xd8, 0x2c, 0x3d, 0x06, 0xce, 0x43, 0x22, 0x9d, 0x57,
0x11, 0xd3, 0xa7, 0xd0, 0x14, 0xd7, 0x59, 0xd2, 0x6c, 0x77, 0x51, 0x36, 0xce, 0x2b, 0x6e, 0xc1,
0xd2, 0x8e, 0x3e, 0x80, 0xb6, 0xc0, 0xd3, 0x85, 0x3f, 0x21, 0x32, 0xdb, 0x36, 0xbe, 0xdf, 0xf0,
0x5e, 0xaf, 0x7b, 0xed, 0xc5, 0x11, 0x23, 0x11, 0xdb, 0x2c, 0xfe, 0x01, 0x9c, 0x16, 0x30, 0xe9,
0x04, 0xce, 0xa1, 0xa5, 0x43, 0x93, 0x6c, 0xa5, 0xba, 0x1a, 0x94, 0xf7, 0x2f, 0x2f, 0xf1, 0xf5,
0x62, 0xea, 0x33, 0x62, 0x4c, 0xff, 0x13, 0xd4, 0x73, 0x5e, 0x76, 0x31, 0x16, 0xb4, 0x16, 0x07,
0x8a, 0x5b, 0xcd, 0x8e, 0x9e, 0xf8, 0xc5, 0xca, 0x8e, 0x5e, 0x80, 0xb5, 0xf2, 0x43, 0xce, 0x23,
0x85, 0x48, 0x55, 0xd3, 0x48, 0x39, 0x53, 0xb0, 0x46, 0xa0, 0x13, 0x68, 0x4d, 0x93, 0xbb, 0x71,
0xb2, 0x8c, 0x64, 0x93, 0xd9, 0xd8, 0xe2, 0x4b, 0xbc, 0x8c, 0xd0, 0xc7, 0xb0, 0x3b, 0x0d, 0xa8,
0x7f, 0x13, 0x92, 0xf1, 0x3c, 0x8e, 0x7f, 0xa3, 0xb2, 0xcf, 0x6c, 0xbc, 0xa3, 0x37, 0x5f, 0x8b,
0x3d, 0xe4, 0x82, 0xcd, 0x79, 0x02, 0x91, 0x00, 0x6f, 0x1e, 0x61, 0x4f, 0xd7, 0x5c, 0xf3, 0xa3,
0x5c, 0x6a, 0x9b, 0xaa, 0xf4, 0x57, 0x1d, 0x8e, 0xae, 0x22, 0xde, 0x7a, 0x61, 0x98, 0x93, 0x29,
0x95, 0xa4, 0x56, 0x59, 0x92, 0xfa, 0xfb, 0x48, 0xd2, 0xc8, 0x48, 0x62, 0x8a, 0xd2, 0x5c, 0x2b,
0x4a, 0x25, 0x99, 0x32, 0x97, 0xd3, 0xca, 0x5d, 0x4e, 0xf4, 0x21, 0x40, 0x42, 0x96, 0x94, 0x8c,
0x25, 0x79, 0x4b, 0x9e, 0x6f, 0xcb, 0x9d, 0xa1, 0xf0, 0xb0, 0xae, 0xb1, 0x9d, 0xd3, 0xf8, 0x0a,
0x8e, 0xf3, 0xc2, 0x6c, 0x2a, 0xf2, 0x1c, 0x4e, 0xae, 0xa3, 0xa0, 0x50, 0xe5, 0xa2, 0xcb, 0xf8,
0x20, 0xef, 0x7a, 0x41, 0xde, 0x7c, 0xfc, 0x2c, 0x96, 0xc9, 0x3b, 0xa2, 0x75, 0x54, 0x0b, 0xef,
0x0d, 0x38, 0x0f, 0x3d, 0x6d, 0x1a, 0xf6, 0x53, 0x38, 0xe0, 0xfd, 0xf8, 0xa3, 0xea, 0x4e, 0x1d,
0xb0, 0xd7, 0x07, 0xb4, 0xbe, 0x79, 0xcf, 0xad, 0xb7, 0xb2, 0xdc, 0xe6, 0xd3, 0x67, 0xf0, 0xa6,
0xd7, 0xbb, 0x7f, 0x5b, 0xb0, 0x67, 0x26, 0x95, 0xfa, 0xae, 0xa0, 0x00, 0x76, 0xd6, 0x47, 0x32,
0xfa, 0xac, 0xfc, 0xb3, 0x93, 0xfb, 0x76, 0xba, 0x2f, 0xaa, 0x40, 0x55, 0xa8, 0xde, 0x93, 0xaf,
0x6a, 0x88, 0xc2, 0x7e, 0x7e, 0x52, 0xa2, 0x2f, 0x8b, 0x39, 0x4a, 0x46, 0xb3, 0xdb, 0xa9, 0x0a,
0x37, 0x6e, 0xd1, 0x4a, 0xca, 0x99, 0x1d, 0x6f, 0xe8, 0x51, 0x9a, 0xec, 0x44, 0x75, 0xcf, 0x2b,
0xe3, 0x53, 0xbf, 0xbf, 0xc2, 0x6e, 0x66, 0x58, 0xa0, 0x12, 0xb5, 0x8a, 0x86, 0xa5, 0xfb, 0x79,
0x25, 0x6c, 0xea, 0xeb, 0x16, 0xf6, 0xb2, 0x4d, 0x83, 0x4a, 0x08, 0x0a, 0x67, 0x8e, 0xfb, 0x45,
0x35, 0x70, 0xea, 0x8e, 0xd7, 0x31, 0x7f, 0xdd, 0xcb, 0xea, 0x58, 0xd2, 0x80, 0x65, 0x75, 0x2c,
0xeb, 0x22, 0xee, 0xd4, 0x07, 0xb8, 0xef, 0x00, 0xf4, 0xbc, 0xb4, 0x20, 0xd9, 0xc6, 0x71, 0xcf,
0x1e, 0x07, 0x1a, 0x17, 0x2f, 0xe1, 0x67, 0xdb, 0xe0, 0x6e, 0x2c, 0xf9, 0xb7, 0xf1, 0x9b, 0xff,
0x02, 0x00, 0x00, 0xff, 0xff, 0x7b, 0x5d, 0x13, 0x1a, 0x07, 0x0b, 0x00, 0x00,
>>>>>>> Add validate to Install / Upgrade commands that use kubernets to validate the manifest before reifying
} }

Loading…
Cancel
Save