diff --git a/cmd/kubeadm/app/util/marshal.go b/cmd/kubeadm/app/util/marshal.go index 67281d04732..0d2d8df594d 100644 --- a/cmd/kubeadm/app/util/marshal.go +++ b/cmd/kubeadm/app/util/marshal.go @@ -41,3 +41,20 @@ func MarshalToYamlForCodecs(obj runtime.Object, gv schema.GroupVersion, codecs s encoder := codecs.EncoderForVersion(info.Serializer, gv) return runtime.Encode(encoder, obj) } + +// UnmarshalFromYaml unmarshals yaml into an object. +func UnmarshalFromYaml(buffer []byte, gv schema.GroupVersion) (runtime.Object, error) { + return UnmarshalFromYamlForCodecs(buffer, gv, clientsetscheme.Codecs) +} + +// UnmarshalFromYamlForCodecs unmarshals yaml into an object using the specified codec +func UnmarshalFromYamlForCodecs(buffer []byte, gv schema.GroupVersion, codecs serializer.CodecFactory) (runtime.Object, error) { + mediaType := "application/yaml" + info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType) + if !ok { + return nil, fmt.Errorf("unsupported media type %q", mediaType) + } + + decoder := codecs.DecoderToVersion(info.Serializer, gv) + return runtime.Decode(decoder, buffer) +} diff --git a/cmd/kubeadm/app/util/staticpod/BUILD b/cmd/kubeadm/app/util/staticpod/BUILD index 19256215f48..fb15448d198 100644 --- a/cmd/kubeadm/app/util/staticpod/BUILD +++ b/cmd/kubeadm/app/util/staticpod/BUILD @@ -14,6 +14,7 @@ go_test( "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", "//cmd/kubeadm/app/features:go_default_library", + "//cmd/kubeadm/test:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", diff --git a/cmd/kubeadm/app/util/staticpod/utils.go b/cmd/kubeadm/app/util/staticpod/utils.go index 08291f92709..57b1b01377a 100644 --- a/cmd/kubeadm/app/util/staticpod/utils.go +++ b/cmd/kubeadm/app/util/staticpod/utils.go @@ -198,6 +198,23 @@ func WriteStaticPodToDisk(componentName, manifestDir string, pod v1.Pod) error { return nil } +// ReadStaticPodFromDisk reads a static pod file from disk +func ReadStaticPodFromDisk(manifestPath string) (*v1.Pod, error) { + buf, err := ioutil.ReadFile(manifestPath) + if err != nil { + return &v1.Pod{}, fmt.Errorf("failed to read manifest for %q: %v", manifestPath, err) + } + + obj, err := util.UnmarshalFromYaml(buf, v1.SchemeGroupVersion) + if err != nil { + return &v1.Pod{}, fmt.Errorf("failed to unmarshal manifest for %q from YAML: %v", manifestPath, err) + } + + pod := obj.(*v1.Pod) + + return pod, nil +} + // GetProbeAddress returns an IP address or 127.0.0.1 to use for liveness probes // in static pod manifests. func GetProbeAddress(cfg *kubeadmapi.MasterConfiguration, componentName string) string { diff --git a/cmd/kubeadm/app/util/staticpod/utils_test.go b/cmd/kubeadm/app/util/staticpod/utils_test.go index 1cf4da2b071..7d9bd315b0c 100644 --- a/cmd/kubeadm/app/util/staticpod/utils_test.go +++ b/cmd/kubeadm/app/util/staticpod/utils_test.go @@ -17,6 +17,9 @@ limitations under the License. package staticpod import ( + "io/ioutil" + "os" + "path/filepath" "reflect" "sort" "testing" @@ -28,6 +31,7 @@ import ( kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" + testutil "k8s.io/kubernetes/cmd/kubeadm/test" ) func TestComponentResources(t *testing.T) { @@ -454,3 +458,73 @@ func TestGetExtraParameters(t *testing.T) { } } } + +const ( + validPod = ` +apiVersion: v1 +kind: Pod +metadata: + labels: + component: etcd + tier: control-plane + name: etcd + namespace: kube-system +spec: + containers: + - image: gcr.io/google_containers/etcd-amd64:3.1.11 +status: {} +` + invalidPod = `---{ broken yaml @@@` +) + +func TestReadStaticPodFromDisk(t *testing.T) { + tests := []struct { + description string + podYaml string + expectErr bool + writeManifest bool + }{ + { + description: "valid pod is marshaled", + podYaml: validPod, + writeManifest: true, + expectErr: false, + }, + { + description: "invalid pod fails to unmarshal", + podYaml: invalidPod, + writeManifest: true, + expectErr: true, + }, + { + description: "non-existent file returns error", + podYaml: ``, + writeManifest: false, + expectErr: true, + }, + } + + for _, rt := range tests { + tmpdir := testutil.SetupTempDir(t) + defer os.RemoveAll(tmpdir) + + manifestPath := filepath.Join(tmpdir, "pod.yaml") + if rt.writeManifest { + err := ioutil.WriteFile(manifestPath, []byte(rt.podYaml), 0644) + if err != nil { + t.Fatalf("Failed to write pod manifest\n%s\n\tfatal error: %v", rt.description, err) + } + } + + _, actualErr := ReadStaticPodFromDisk(manifestPath) + if (actualErr != nil) != rt.expectErr { + t.Errorf( + "ReadStaticPodFromDisk failed\n%s\n\texpected error: %t\n\tgot: %t\n\tactual error: %v", + rt.description, + rt.expectErr, + (actualErr != nil), + actualErr, + ) + } + } +}