Merge pull request #3556 from ryane/fix/init-output-service

fix(helm): add service, secret manifests in `init -o`
pull/4407/head v2.10.0-rc.2
Matthew Fisher 7 years ago committed by GitHub
commit 56154102a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -181,95 +181,64 @@ func (i *initCmd) run() error {
i.opts.MaxHistory = i.maxHistory
i.opts.Replicas = i.replicas
writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error {
writeYAMLManifests := func(manifests []string) error {
w := i.out
if !first {
// YAML starting document boundary marker
for _, manifest := range manifests {
if _, err := fmt.Fprintln(w, "---"); err != nil {
return err
}
if _, err := fmt.Fprintln(w, manifest); err != nil {
return err
}
}
if _, err := fmt.Fprintln(w, "apiVersion:", apiVersion); err != nil {
return err
}
if _, err := fmt.Fprintln(w, "kind:", kind); err != nil {
return err
}
if _, err := fmt.Fprint(w, body); err != nil {
return err
}
if !last {
return nil
}
// YAML ending document boundary marker
_, err := fmt.Fprintln(w, "...")
return err
}
if len(i.opts.Output) > 0 {
var body string
var manifests []string
var err error
const tm = `{"apiVersion":"extensions/v1beta1","kind":"Deployment",`
if body, err = installer.DeploymentManifest(&i.opts); err != nil {
if manifests, err = installer.TillerManifests(&i.opts); err != nil {
return err
}
switch i.opts.Output.String() {
case "json":
var out bytes.Buffer
jsonb, err := yaml.ToJSON([]byte(body))
if err != nil {
return err
}
buf := bytes.NewBuffer(make([]byte, 0, len(tm)+len(jsonb)-1))
buf.WriteString(tm)
// Drop the opening object delimiter ('{').
buf.Write(jsonb[1:])
if err := json.Indent(&out, buf.Bytes(), "", " "); err != nil {
return err
}
if _, err = i.out.Write(out.Bytes()); err != nil {
return err
for _, manifest := range manifests {
var out bytes.Buffer
jsonb, err := yaml.ToJSON([]byte(manifest))
if err != nil {
return err
}
buf := bytes.NewBuffer(jsonb)
if err := json.Indent(&out, buf.Bytes(), "", " "); err != nil {
return err
}
if _, err = i.out.Write(out.Bytes()); err != nil {
return err
}
fmt.Fprint(i.out, "\n")
}
return nil
case "yaml":
if err := writeYAMLManifest("extensions/v1beta1", "Deployment", body, true, false); err != nil {
return err
}
return nil
return writeYAMLManifests(manifests)
default:
return fmt.Errorf("unknown output format: %q", i.opts.Output)
}
}
if settings.Debug {
var body string
var manifests []string
var err error
// write Deployment manifest
if body, err = installer.DeploymentManifest(&i.opts); err != nil {
return err
}
if err := writeYAMLManifest("extensions/v1beta1", "Deployment", body, true, false); err != nil {
// write Tiller manifests
if manifests, err = installer.TillerManifests(&i.opts); err != nil {
return err
}
// write Service manifest
if body, err = installer.ServiceManifest(i.namespace); err != nil {
return err
}
if err := writeYAMLManifest("v1", "Service", body, false, !i.opts.EnableTLS); err != nil {
if err = writeYAMLManifests(manifests); err != nil {
return err
}
// write Secret manifest
if i.opts.EnableTLS {
if body, err = installer.SecretManifest(&i.opts); err != nil {
return err
}
if err := writeYAMLManifest("v1", "Secret", body, false, true); err != nil {
return err
}
}
}
if i.dryRun {

@ -18,6 +18,7 @@ package main
import (
"bytes"
"io"
"io/ioutil"
"os"
"path/filepath"
@ -32,11 +33,10 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
yamlutil "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/kubernetes/fake"
testcore "k8s.io/client-go/testing"
"encoding/json"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/helm/helmpath"
)
@ -306,7 +306,7 @@ func TestInitCmd_tlsOptions(t *testing.T) {
}
}
// TestInitCmd_output tests that init -o formats are unmarshal-able
// TestInitCmd_output tests that init -o can be decoded
func TestInitCmd_output(t *testing.T) {
// This is purely defensive in this case.
home, err := ioutil.TempDir("", "helm_home")
@ -320,26 +320,14 @@ func TestInitCmd_output(t *testing.T) {
settings.Debug = dbg
}()
fc := fake.NewSimpleClientset()
tests := []struct {
expectF func([]byte, interface{}) error
expectName string
}{
{
json.Unmarshal,
"json",
},
{
yaml.Unmarshal,
"yaml",
},
}
tests := []string{"json", "yaml"}
for _, s := range tests {
var buf bytes.Buffer
cmd := &initCmd{
out: &buf,
home: helmpath.Home(home),
kubeClient: fc,
opts: installer.Options{Output: installer.OutputFormat(s.expectName)},
opts: installer.Options{Output: installer.OutputFormat(s)},
namespace: v1.NamespaceDefault,
}
if err := cmd.run(); err != nil {
@ -348,10 +336,17 @@ func TestInitCmd_output(t *testing.T) {
if got := len(fc.Actions()); got != 0 {
t.Errorf("expected no server calls, got %d", got)
}
d := &v1beta1.Deployment{}
if err = s.expectF(buf.Bytes(), &d); err != nil {
t.Errorf("error unmarshalling init %s output %s %s", s.expectName, err, buf.String())
var obj interface{}
decoder := yamlutil.NewYAMLOrJSONDecoder(&buf, 4096)
for {
err := decoder.Decode(&obj)
if err != nil {
if err == io.EOF {
break
}
t.Errorf("error decoding init %s output %s %s", s, err, buf.String())
}
}
}
}

@ -28,6 +28,7 @@ import (
"k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
@ -104,7 +105,7 @@ func semverCompare(image string) int {
// createDeployment creates the Tiller Deployment resource.
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
obj, err := deployment(opts)
obj, err := generateDeployment(opts)
if err != nil {
return err
}
@ -113,40 +114,68 @@ func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options)
}
// deployment gets the deployment object that installs Tiller.
func deployment(opts *Options) (*v1beta1.Deployment, error) {
return generateDeployment(opts)
// Deployment gets a deployment object that can be used to generate a manifest
// as a string. This object should not be submitted directly to the Kubernetes
// api
func Deployment(opts *Options) (*v1beta1.Deployment, error) {
dep, err := generateDeployment(opts)
if err != nil {
return nil, err
}
dep.TypeMeta = metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
}
return dep, nil
}
// createService creates the Tiller service resource
func createService(client corev1.ServicesGetter, namespace string) error {
obj := service(namespace)
obj := generateService(namespace)
_, err := client.Services(obj.Namespace).Create(obj)
return err
}
// service gets the service object that installs Tiller.
func service(namespace string) *v1.Service {
return generateService(namespace)
// Service gets a service object that can be used to generate a manifest as a
// string. This object should not be submitted directly to the Kubernetes api
func Service(namespace string) *v1.Service {
svc := generateService(namespace)
svc.TypeMeta = metav1.TypeMeta{
Kind: "Service",
APIVersion: "v1",
}
return svc
}
// DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment
// resource.
func DeploymentManifest(opts *Options) (string, error) {
obj, err := deployment(opts)
// TillerManifests gets the Deployment, Service, and Secret (if tls-enabled) manifests
func TillerManifests(opts *Options) ([]string, error) {
dep, err := Deployment(opts)
if err != nil {
return "", err
return []string{}, err
}
svc := Service(opts.Namespace)
objs := []runtime.Object{dep, svc}
if opts.EnableTLS {
secret, err := Secret(opts)
if err != nil {
return []string{}, err
}
objs = append(objs, secret)
}
manifests := make([]string, len(objs))
for i, obj := range objs {
o, err := yaml.Marshal(obj)
if err != nil {
return []string{}, err
}
manifests[i] = string(o)
}
buf, err := yaml.Marshal(obj)
return string(buf), err
}
// ServiceManifest gets the manifest (as a string) that describes the Tiller Service
// resource.
func ServiceManifest(namespace string) (string, error) {
obj := service(namespace)
buf, err := yaml.Marshal(obj)
return string(buf), err
return manifests, err
}
func generateLabels(labels map[string]string) map[string]string {
@ -321,14 +350,20 @@ func generateService(namespace string) *v1.Service {
return s
}
// SecretManifest gets the manifest (as a string) that describes the Tiller Secret resource.
func SecretManifest(opts *Options) (string, error) {
o, err := generateSecret(opts)
// Secret gets a secret object that can be used to generate a manifest as a
// string. This object should not be submitted directly to the Kubernetes api
func Secret(opts *Options) (*v1.Secret, error) {
secret, err := generateSecret(opts)
if err != nil {
return "", err
return nil, err
}
secret.TypeMeta = metav1.TypeMeta{
Kind: "Secret",
APIVersion: "v1",
}
buf, err := yaml.Marshal(o)
return string(buf), err
return secret, nil
}
// createSecret creates the Tiller secret resource.

@ -34,7 +34,7 @@ import (
"k8s.io/helm/pkg/version"
)
func TestDeploymentManifest(t *testing.T) {
func TestDeployment(t *testing.T) {
tests := []struct {
name string
image string
@ -48,14 +48,10 @@ func TestDeploymentManifest(t *testing.T) {
}
for _, tt := range tests {
o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary})
dep, err := Deployment(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary})
if err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
var dep v1beta1.Deployment
if err := yaml.Unmarshal([]byte(o), &dep); err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
if got := dep.Spec.Template.Spec.Containers[0].Image; got != tt.expect {
t.Errorf("%s: expected image %q, got %q", tt.name, tt.expect, got)
@ -71,7 +67,7 @@ func TestDeploymentManifest(t *testing.T) {
}
}
func TestDeploymentManifestForServiceAccount(t *testing.T) {
func TestDeploymentForServiceAccount(t *testing.T) {
tests := []struct {
name string
image string
@ -84,22 +80,18 @@ func TestDeploymentManifestForServiceAccount(t *testing.T) {
{"withoutSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", ""},
}
for _, tt := range tests {
o, err := DeploymentManifest(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount})
d, err := Deployment(&Options{Namespace: v1.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount})
if err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
var d v1beta1.Deployment
if err := yaml.Unmarshal([]byte(o), &d); err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
if got := d.Spec.Template.Spec.ServiceAccountName; got != tt.serviceAccount {
t.Errorf("%s: expected service account value %q, got %q", tt.name, tt.serviceAccount, got)
}
}
}
func TestDeploymentManifest_WithTLS(t *testing.T) {
func TestDeployment_WithTLS(t *testing.T) {
tests := []struct {
opts Options
name string
@ -126,15 +118,11 @@ func TestDeploymentManifest_WithTLS(t *testing.T) {
},
}
for _, tt := range tests {
o, err := DeploymentManifest(&tt.opts)
d, err := Deployment(&tt.opts)
if err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
var d v1beta1.Deployment
if err := yaml.Unmarshal([]byte(o), &d); err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
// verify environment variable in deployment reflect the use of tls being enabled.
if got := d.Spec.Template.Spec.Containers[0].Env[2].Value; got != tt.verify {
t.Errorf("%s: expected tls verify env value %q, got %q", tt.name, tt.verify, got)
@ -146,14 +134,7 @@ func TestDeploymentManifest_WithTLS(t *testing.T) {
}
func TestServiceManifest(t *testing.T) {
o, err := ServiceManifest(v1.NamespaceDefault)
if err != nil {
t.Fatalf("error %q", err)
}
var svc v1.Service
if err := yaml.Unmarshal([]byte(o), &svc); err != nil {
t.Fatalf("error %q", err)
}
svc := Service(v1.NamespaceDefault)
if got := svc.ObjectMeta.Namespace; got != v1.NamespaceDefault {
t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got)
@ -161,7 +142,7 @@ func TestServiceManifest(t *testing.T) {
}
func TestSecretManifest(t *testing.T) {
o, err := SecretManifest(&Options{
obj, err := Secret(&Options{
VerifyTLS: true,
EnableTLS: true,
Namespace: v1.NamespaceDefault,
@ -174,11 +155,6 @@ func TestSecretManifest(t *testing.T) {
t.Fatalf("error %q", err)
}
var obj v1.Secret
if err := yaml.Unmarshal([]byte(o), &obj); err != nil {
t.Fatalf("error %q", err)
}
if got := obj.ObjectMeta.Namespace; got != v1.NamespaceDefault {
t.Errorf("expected namespace %s, got %s", v1.NamespaceDefault, got)
}
@ -362,13 +338,13 @@ func TestInstall_canary(t *testing.T) {
func TestUpgrade(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
existingDeployment, _ := generateDeployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace:v1.0.0",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
existingService := service(v1.NamespaceDefault)
existingService := generateService(v1.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
@ -403,7 +379,7 @@ func TestUpgrade(t *testing.T) {
func TestUpgrade_serviceNotFound(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
existingDeployment, _ := deployment(&Options{
existingDeployment, _ := generateDeployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace",
UseCanary: false,
@ -446,13 +422,13 @@ func TestUpgrade_serviceNotFound(t *testing.T) {
func TestUgrade_newerVersion(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
existingDeployment, _ := generateDeployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace:v100.5.0",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
existingService := service(v1.NamespaceDefault)
existingService := generateService(v1.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
@ -506,13 +482,13 @@ func TestUgrade_newerVersion(t *testing.T) {
func TestUpgrade_identical(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
existingDeployment, _ := generateDeployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace:v2.0.0",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
existingService := service(v1.NamespaceDefault)
existingService := generateService(v1.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
@ -547,13 +523,13 @@ func TestUpgrade_identical(t *testing.T) {
func TestUpgrade_canaryClient(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:canary"
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
existingDeployment, _ := generateDeployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace:v1.0.0",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
existingService := service(v1.NamespaceDefault)
existingService := generateService(v1.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
@ -588,13 +564,13 @@ func TestUpgrade_canaryClient(t *testing.T) {
func TestUpgrade_canaryServer(t *testing.T) {
image := "gcr.io/kubernetes-helm/tiller:v2.0.0"
serviceAccount := "newServiceAccount"
existingDeployment, _ := deployment(&Options{
existingDeployment, _ := generateDeployment(&Options{
Namespace: v1.NamespaceDefault,
ImageSpec: "imageToReplace:canary",
ServiceAccount: "serviceAccountToReplace",
UseCanary: false,
})
existingService := service(v1.NamespaceDefault)
existingService := generateService(v1.NamespaceDefault)
fc := &fake.Clientset{}
fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
@ -634,7 +610,8 @@ func tlsTestFile(t *testing.T, path string) string {
}
return path
}
func TestDeploymentManifest_WithNodeSelectors(t *testing.T) {
func TestDeployment_WithNodeSelectors(t *testing.T) {
tests := []struct {
opts Options
name string
@ -658,15 +635,11 @@ func TestDeploymentManifest_WithNodeSelectors(t *testing.T) {
},
}
for _, tt := range tests {
o, err := DeploymentManifest(&tt.opts)
d, err := Deployment(&tt.opts)
if err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
var d v1beta1.Deployment
if err := yaml.Unmarshal([]byte(o), &d); err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
// Verify that environment variables in Deployment reflect the use of TLS being enabled.
got := d.Spec.Template.Spec.NodeSelector
for k, v := range tt.expect {
@ -676,7 +649,8 @@ func TestDeploymentManifest_WithNodeSelectors(t *testing.T) {
}
}
}
func TestDeploymentManifest_WithSetValues(t *testing.T) {
func TestDeployment_WithSetValues(t *testing.T) {
tests := []struct {
opts Options
name string
@ -703,11 +677,17 @@ func TestDeploymentManifest_WithSetValues(t *testing.T) {
},
}
for _, tt := range tests {
o, err := DeploymentManifest(&tt.opts)
d, err := Deployment(&tt.opts)
if err != nil {
t.Fatalf("%s: error %q", tt.name, err)
}
values, err := chartutil.ReadValues([]byte(o))
o, err := yaml.Marshal(d)
if err != nil {
t.Errorf("Error marshaling Deployment: %s", err)
}
values, err := chartutil.ReadValues(o)
if err != nil {
t.Errorf("Error converting Deployment manifest to Values: %s", err)
}

Loading…
Cancel
Save