From 8cee92dfa8510613dce2096a70ce2594f9254078 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Tue, 28 May 2019 18:54:41 +0300 Subject: [PATCH 1/4] create new api group output.kubeadm.k8s.io This group contains APIs for handling kubeadm structured output. --- cmd/kubeadm/app/BUILD | 1 + cmd/kubeadm/app/apis/output/BUILD | 38 ++++++++++ cmd/kubeadm/app/apis/output/doc.go | 24 +++++++ cmd/kubeadm/app/apis/output/fuzzer/BUILD | 43 ++++++++++++ cmd/kubeadm/app/apis/output/fuzzer/fuzzer.go | 45 ++++++++++++ .../app/apis/output/fuzzer/fuzzer_test.go | 28 ++++++++ cmd/kubeadm/app/apis/output/register.go | 52 ++++++++++++++ cmd/kubeadm/app/apis/output/scheme/BUILD | 31 +++++++++ cmd/kubeadm/app/apis/output/scheme/scheme.go | 45 ++++++++++++ cmd/kubeadm/app/apis/output/types.go | 34 +++++++++ cmd/kubeadm/app/apis/output/v1alpha1/BUILD | 38 ++++++++++ .../app/apis/output/v1alpha1/defaults.go | 25 +++++++ cmd/kubeadm/app/apis/output/v1alpha1/doc.go | 27 ++++++++ .../app/apis/output/v1alpha1/register.go | 65 +++++++++++++++++ cmd/kubeadm/app/apis/output/v1alpha1/types.go | 34 +++++++++ .../v1alpha1/zz_generated.conversion.go | 69 +++++++++++++++++++ .../output/v1alpha1/zz_generated.deepcopy.go | 52 ++++++++++++++ .../output/v1alpha1/zz_generated.defaults.go | 38 ++++++++++ .../app/apis/output/zz_generated.deepcopy.go | 52 ++++++++++++++ cmd/kubeadm/app/cmd/BUILD | 12 ++-- 20 files changed, 746 insertions(+), 7 deletions(-) create mode 100644 cmd/kubeadm/app/apis/output/BUILD create mode 100644 cmd/kubeadm/app/apis/output/doc.go create mode 100644 cmd/kubeadm/app/apis/output/fuzzer/BUILD create mode 100644 cmd/kubeadm/app/apis/output/fuzzer/fuzzer.go create mode 100644 cmd/kubeadm/app/apis/output/fuzzer/fuzzer_test.go create mode 100644 cmd/kubeadm/app/apis/output/register.go create mode 100644 cmd/kubeadm/app/apis/output/scheme/BUILD create mode 100644 cmd/kubeadm/app/apis/output/scheme/scheme.go create mode 100644 cmd/kubeadm/app/apis/output/types.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/BUILD create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/defaults.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/doc.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/register.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/types.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go create mode 100644 cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.defaults.go create mode 100644 cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go diff --git a/cmd/kubeadm/app/BUILD b/cmd/kubeadm/app/BUILD index 7a1263a5875..789b741daa3 100644 --- a/cmd/kubeadm/app/BUILD +++ b/cmd/kubeadm/app/BUILD @@ -29,6 +29,7 @@ filegroup( srcs = [ ":package-srcs", "//cmd/kubeadm/app/apis/kubeadm:all-srcs", + "//cmd/kubeadm/app/apis/output:all-srcs", "//cmd/kubeadm/app/cmd:all-srcs", "//cmd/kubeadm/app/componentconfigs:all-srcs", "//cmd/kubeadm/app/constants:all-srcs", diff --git a/cmd/kubeadm/app/apis/output/BUILD b/cmd/kubeadm/app/apis/output/BUILD new file mode 100644 index 00000000000..a8122ef7eff --- /dev/null +++ b/cmd/kubeadm/app/apis/output/BUILD @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "register.go", + "types.go", + "zz_generated.deepcopy.go", + ], + importpath = "k8s.io/kubernetes/cmd/kubeadm/app/apis/output", + visibility = ["//visibility:public"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [ + ":package-srcs", + "//cmd/kubeadm/app/apis/output/fuzzer:all-srcs", + "//cmd/kubeadm/app/apis/output/scheme:all-srcs", + "//cmd/kubeadm/app/apis/output/v1alpha1:all-srcs", + ], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/cmd/kubeadm/app/apis/output/doc.go b/cmd/kubeadm/app/apis/output/doc.go new file mode 100644 index 00000000000..eaca8994d2a --- /dev/null +++ b/cmd/kubeadm/app/apis/output/doc.go @@ -0,0 +1,24 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package +// +groupName=output.kubeadm.k8s.io + +// Package output implements the kubeadm structured output +// The purpose of the kubeadm structured output is to have a well +// defined versioned output format that other software that uses +// kubeadm for cluster deployments can use and rely on. +package output // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/output" diff --git a/cmd/kubeadm/app/apis/output/fuzzer/BUILD b/cmd/kubeadm/app/apis/output/fuzzer/BUILD new file mode 100644 index 00000000000..ec666ca0101 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/fuzzer/BUILD @@ -0,0 +1,43 @@ +package(default_visibility = ["//visibility:public"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test", +) + +go_library( + name = "go_default_library", + srcs = ["fuzzer.go"], + importpath = "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/fuzzer", + deps = [ + "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library", + "//cmd/kubeadm/app/apis/output:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//vendor/github.com/google/gofuzz:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) + +go_test( + name = "go_default_test", + srcs = ["fuzzer_test.go"], + embed = [":go_default_library"], + deps = [ + "//cmd/kubeadm/app/apis/output/scheme:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip:go_default_library", + ], +) diff --git a/cmd/kubeadm/app/apis/output/fuzzer/fuzzer.go b/cmd/kubeadm/app/apis/output/fuzzer/fuzzer.go new file mode 100644 index 00000000000..ab1d312def2 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/fuzzer/fuzzer.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fuzzer + +import ( + "time" + + fuzz "github.com/google/gofuzz" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/output" +) + +// Funcs returns the fuzzer functions for the kubeadm apis. +func Funcs(codecs runtimeserializer.CodecFactory) []interface{} { + return []interface{}{ + fuzzBootstrapToken, + } +} + +func fuzzBootstrapToken(obj *output.BootstrapToken, c fuzz.Continue) { + c.FuzzNoCustom(obj) + + obj.ObjectMeta = metav1.ObjectMeta{} + obj.Token = &kubeadmapiv1beta2.BootstrapTokenString{ID: "uvxdac", Secret: "fq35fuyue3kd4gda"} + obj.Description = "" + obj.TTL = &metav1.Duration{Duration: time.Hour * 24} + obj.Usages = []string{"authentication", "signing"} + obj.Groups = []string{"system:bootstrappers:kubeadm:default-node-token"} +} diff --git a/cmd/kubeadm/app/apis/output/fuzzer/fuzzer_test.go b/cmd/kubeadm/app/apis/output/fuzzer/fuzzer_test.go new file mode 100644 index 00000000000..0afa00fb802 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/fuzzer/fuzzer_test.go @@ -0,0 +1,28 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package fuzzer + +import ( + "testing" + + "k8s.io/apimachinery/pkg/api/apitesting/roundtrip" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" +) + +func TestRoundTripTypes(t *testing.T) { + roundtrip.RoundTripTestForAPIGroup(t, scheme.AddToScheme, Funcs) +} diff --git a/cmd/kubeadm/app/apis/output/register.go b/cmd/kubeadm/app/apis/output/register.go new file mode 100644 index 00000000000..111f32df3b0 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/register.go @@ -0,0 +1,52 @@ +/* +Copyright 2016 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package output + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "output.kubeadm.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal} + +var ( + // SchemeBuilder points to a list of functions added to Scheme. + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + // AddToScheme applies all the stored functions to the scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +// Kind takes an unqualified kind and returns a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &BootstrapToken{}, + ) + return nil +} diff --git a/cmd/kubeadm/app/apis/output/scheme/BUILD b/cmd/kubeadm/app/apis/output/scheme/BUILD new file mode 100644 index 00000000000..ab020240801 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/scheme/BUILD @@ -0,0 +1,31 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["scheme.go"], + importpath = "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme", + visibility = ["//visibility:public"], + deps = [ + "//cmd/kubeadm/app/apis/output:go_default_library", + "//cmd/kubeadm/app/apis/output/v1alpha1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/cmd/kubeadm/app/apis/output/scheme/scheme.go b/cmd/kubeadm/app/apis/output/scheme/scheme.go new file mode 100644 index 00000000000..053dc1bc1a3 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/scheme/scheme.go @@ -0,0 +1,45 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package scheme + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/output" + "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1" +) + +// Scheme is the runtime.Scheme to which all kubeadm api types are registered. +var Scheme = runtime.NewScheme() + +// Codecs provides access to encoding and decoding for the scheme. +var Codecs = serializer.NewCodecFactory(Scheme) + +func init() { + metav1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + AddToScheme(Scheme) +} + +// AddToScheme builds the kubeadm scheme using all known versions of the kubeadm api. +func AddToScheme(scheme *runtime.Scheme) { + utilruntime.Must(output.AddToScheme(scheme)) + utilruntime.Must(v1alpha1.AddToScheme(scheme)) + utilruntime.Must(scheme.SetVersionPriority(v1alpha1.SchemeGroupVersion)) +} diff --git a/cmd/kubeadm/app/apis/output/types.go b/cmd/kubeadm/app/apis/output/types.go new file mode 100644 index 00000000000..9acb6602202 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/types.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package output + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// BootstrapToken represents information for the output produced by 'kubeadm token list' +// This is a copy of BoostrapToken struct from ../kubeadm/types.go with 2 additions: +// metav1.TypeMeta and metav1.ObjectMeta +type BootstrapToken struct { + metav1.TypeMeta + metav1.ObjectMeta + + kubeadmapiv1beta2.BootstrapToken +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/BUILD b/cmd/kubeadm/app/apis/output/v1alpha1/BUILD new file mode 100644 index 00000000000..4d78b59c96d --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/BUILD @@ -0,0 +1,38 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = [ + "defaults.go", + "doc.go", + "register.go", + "types.go", + "zz_generated.conversion.go", + "zz_generated.deepcopy.go", + "zz_generated.defaults.go", + ], + importpath = "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1", + visibility = ["//visibility:public"], + deps = [ + "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library", + "//cmd/kubeadm/app/apis/output:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/defaults.go b/cmd/kubeadm/app/apis/output/v1alpha1/defaults.go new file mode 100644 index 00000000000..720ecdfb5e5 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/defaults.go @@ -0,0 +1,25 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/doc.go b/cmd/kubeadm/app/apis/output/v1alpha1/doc.go new file mode 100644 index 00000000000..875ac45e583 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/doc.go @@ -0,0 +1,27 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:defaulter-gen=TypeMeta +// +groupName=output.kubeadm.k8s.io +// +k8s:deepcopy-gen=package +// +k8s:conversion-gen=k8s.io/kubernetes/cmd/kubeadm/app/apis/output + +// Package v1alpha1 defines the v1alpha1 version of the kubeadm data structures +// related to structured output +// The purpose of the kubeadm structured output is to have a well +// defined versioned output format that other software that uses +// kubeadm for cluster deployments can use and rely on. +package v1alpha1 // import "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1" diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/register.go b/cmd/kubeadm/app/apis/output/v1alpha1/register.go new file mode 100644 index 00000000000..5aa4348521f --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/register.go @@ -0,0 +1,65 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// GroupName is the group name use in this package +const GroupName = "output.kubeadm.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"} + +var ( + // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + + // SchemeBuilder points to a list of functions added to Scheme. + SchemeBuilder runtime.SchemeBuilder + localSchemeBuilder = &SchemeBuilder + // AddToScheme applies all the stored functions to the scheme. + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs) +} + +// Kind takes an unqualified kind and returns a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &BootstrapToken{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/types.go b/cmd/kubeadm/app/apis/output/v1alpha1/types.go new file mode 100644 index 00000000000..e3774c4f7bf --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/types.go @@ -0,0 +1,34 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" +) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// BootstrapToken represents information for the bootstrap token output produced by kubeadm +// This is a copy of BoostrapToken struct from ../kubeadm/types.go with 2 additions: +// metav1.TypeMeta and metav1.ObjectMeta +type BootstrapToken struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:",inline"` + + kubeadmapiv1beta2.BootstrapToken +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go new file mode 100644 index 00000000000..4bd3af5580b --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.conversion.go @@ -0,0 +1,69 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by conversion-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + conversion "k8s.io/apimachinery/pkg/conversion" + runtime "k8s.io/apimachinery/pkg/runtime" + output "k8s.io/kubernetes/cmd/kubeadm/app/apis/output" +) + +func init() { + localSchemeBuilder.Register(RegisterConversions) +} + +// RegisterConversions adds conversion functions to the given scheme. +// Public to allow building arbitrary schemes. +func RegisterConversions(s *runtime.Scheme) error { + if err := s.AddGeneratedConversionFunc((*BootstrapToken)(nil), (*output.BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1alpha1_BootstrapToken_To_output_BootstrapToken(a.(*BootstrapToken), b.(*output.BootstrapToken), scope) + }); err != nil { + return err + } + if err := s.AddGeneratedConversionFunc((*output.BootstrapToken)(nil), (*BootstrapToken)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_output_BootstrapToken_To_v1alpha1_BootstrapToken(a.(*output.BootstrapToken), b.(*BootstrapToken), scope) + }); err != nil { + return err + } + return nil +} + +func autoConvert_v1alpha1_BootstrapToken_To_output_BootstrapToken(in *BootstrapToken, out *output.BootstrapToken, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.BootstrapToken = in.BootstrapToken + return nil +} + +// Convert_v1alpha1_BootstrapToken_To_output_BootstrapToken is an autogenerated conversion function. +func Convert_v1alpha1_BootstrapToken_To_output_BootstrapToken(in *BootstrapToken, out *output.BootstrapToken, s conversion.Scope) error { + return autoConvert_v1alpha1_BootstrapToken_To_output_BootstrapToken(in, out, s) +} + +func autoConvert_output_BootstrapToken_To_v1alpha1_BootstrapToken(in *output.BootstrapToken, out *BootstrapToken, s conversion.Scope) error { + out.ObjectMeta = in.ObjectMeta + out.BootstrapToken = in.BootstrapToken + return nil +} + +// Convert_output_BootstrapToken_To_v1alpha1_BootstrapToken is an autogenerated conversion function. +func Convert_output_BootstrapToken_To_v1alpha1_BootstrapToken(in *output.BootstrapToken, out *BootstrapToken, s conversion.Scope) error { + return autoConvert_output_BootstrapToken_To_v1alpha1_BootstrapToken(in, out, s) +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..371d244a228 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,52 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.BootstrapToken.DeepCopyInto(&out.BootstrapToken) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapToken. +func (in *BootstrapToken) DeepCopy() *BootstrapToken { + if in == nil { + return nil + } + out := new(BootstrapToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BootstrapToken) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.defaults.go b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.defaults.go new file mode 100644 index 00000000000..8cdf1335c17 --- /dev/null +++ b/cmd/kubeadm/app/apis/output/v1alpha1/zz_generated.defaults.go @@ -0,0 +1,38 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by defaulter-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" + v1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" +) + +// RegisterDefaults adds defaulters functions to the given scheme. +// Public to allow building arbitrary schemes. +// All generated defaulters are covering - they call all nested defaulters. +func RegisterDefaults(scheme *runtime.Scheme) error { + scheme.AddTypeDefaultingFunc(&BootstrapToken{}, func(obj interface{}) { SetObjectDefaults_BootstrapToken(obj.(*BootstrapToken)) }) + return nil +} + +func SetObjectDefaults_BootstrapToken(in *BootstrapToken) { + v1beta2.SetDefaults_BootstrapToken(&in.BootstrapToken) +} diff --git a/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go b/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go new file mode 100644 index 00000000000..3fc2460edff --- /dev/null +++ b/cmd/kubeadm/app/apis/output/zz_generated.deepcopy.go @@ -0,0 +1,52 @@ +// +build !ignore_autogenerated + +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package output + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BootstrapToken) DeepCopyInto(out *BootstrapToken) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.BootstrapToken.DeepCopyInto(&out.BootstrapToken) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BootstrapToken. +func (in *BootstrapToken) DeepCopy() *BootstrapToken { + if in == nil { + return nil + } + out := new(BootstrapToken) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BootstrapToken) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index 611a9cbdad5..4ee953eea34 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -1,10 +1,4 @@ -package(default_visibility = ["//visibility:public"]) - -load( - "@io_bazel_rules_go//go:def.bzl", - "go_library", - "go_test", -) +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "go_default_library", @@ -19,6 +13,7 @@ go_library( "version.go", ], importpath = "k8s.io/kubernetes/cmd/kubeadm/app/cmd", + visibility = ["//visibility:public"], deps = [ "//cmd/kubeadm/app/apis/kubeadm:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library", @@ -81,6 +76,8 @@ go_test( embed = [":go_default_library"], deps = [ "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library", + "//cmd/kubeadm/app/apis/output/scheme:go_default_library", + "//cmd/kubeadm/app/apis/output/v1alpha1:go_default_library", "//cmd/kubeadm/app/cmd/options:go_default_library", "//cmd/kubeadm/app/componentconfigs:go_default_library", "//cmd/kubeadm/app/constants:go_default_library", @@ -123,4 +120,5 @@ filegroup( "//cmd/kubeadm/app/cmd/util:all-srcs", ], tags = ["automanaged"], + visibility = ["//visibility:public"], ) From 3765f210129d58bc3eebca4833c6e921fa6908c9 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Thu, 19 Sep 2019 15:06:13 +0300 Subject: [PATCH 2/4] Add internal kubeadm output API Added internal structures and APIs to handle kubeadm structured output. --- cmd/kubeadm/app/cmd/BUILD | 1 + cmd/kubeadm/app/util/BUILD | 1 + cmd/kubeadm/app/util/output/BUILD | 28 ++++ cmd/kubeadm/app/util/output/output.go | 192 ++++++++++++++++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 cmd/kubeadm/app/util/output/BUILD create mode 100644 cmd/kubeadm/app/util/output/output.go diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index 4ee953eea34..879e2b1060a 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -84,6 +84,7 @@ go_test( "//cmd/kubeadm/app/features:go_default_library", "//cmd/kubeadm/app/util:go_default_library", "//cmd/kubeadm/app/util/config:go_default_library", + "//cmd/kubeadm/app/util/output:go_default_library", "//cmd/kubeadm/app/util/runtime:go_default_library", "//staging/src/k8s.io/api/core/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library", diff --git a/cmd/kubeadm/app/util/BUILD b/cmd/kubeadm/app/util/BUILD index bff571735c8..b240ed6f083 100644 --- a/cmd/kubeadm/app/util/BUILD +++ b/cmd/kubeadm/app/util/BUILD @@ -84,6 +84,7 @@ filegroup( "//cmd/kubeadm/app/util/initsystem:all-srcs", "//cmd/kubeadm/app/util/kubeconfig:all-srcs", "//cmd/kubeadm/app/util/kustomize:all-srcs", + "//cmd/kubeadm/app/util/output:all-srcs", "//cmd/kubeadm/app/util/pkiutil:all-srcs", "//cmd/kubeadm/app/util/pubkeypin:all-srcs", "//cmd/kubeadm/app/util/runtime:all-srcs", diff --git a/cmd/kubeadm/app/util/output/BUILD b/cmd/kubeadm/app/util/output/BUILD new file mode 100644 index 00000000000..6a3c10e758a --- /dev/null +++ b/cmd/kubeadm/app/util/output/BUILD @@ -0,0 +1,28 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["output.go"], + importpath = "k8s.io/kubernetes/cmd/kubeadm/app/util/output", + visibility = ["//visibility:public"], + deps = [ + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library", + "//staging/src/k8s.io/cli-runtime/pkg/printers:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/cmd/kubeadm/app/util/output/output.go b/cmd/kubeadm/app/util/output/output.go new file mode 100644 index 00000000000..c6b3b199709 --- /dev/null +++ b/cmd/kubeadm/app/util/output/output.go @@ -0,0 +1,192 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package output + +import ( + "fmt" + "io" + "strings" + + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/cli-runtime/pkg/genericclioptions" + "k8s.io/cli-runtime/pkg/printers" +) + +// TextOutput describes the plain text output +const TextOutput = "text" + +// TextPrintFlags is an iterface to handle custom text output +type TextPrintFlags interface { + ToPrinter(outputFormat string) (Printer, error) +} + +// PrintFlags composes common printer flag structs +// used across kubeadm commands, and provides a method +// of retrieving a known printer based on flag values provided. +type PrintFlags struct { + // JSONYamlPrintFlags provides default flags necessary for json/yaml printing. + JSONYamlPrintFlags *genericclioptions.JSONYamlPrintFlags + // KubeTemplatePrintFlags composes print flags that provide both a JSONPath and a go-template printer. + KubeTemplatePrintFlags *genericclioptions.KubeTemplatePrintFlags + // JSONYamlPrintFlags provides default flags necessary for kubeadm text printing. + TextPrintFlags TextPrintFlags + // TypeSetterPrinter is an implementation of ResourcePrinter that wraps another printer with types set on the objects + TypeSetterPrinter *printers.TypeSetterPrinter + // OutputFormat contains currently set output format + OutputFormat *string + + // OutputFlagSpecified indicates whether the user specifically requested a certain kind of output. + // Using this function allows a sophisticated caller to change the flag binding logic if they so desire. + OutputFlagSpecified func() bool +} + +// AllowedFormats returns list of allowed output formats +func (pf *PrintFlags) AllowedFormats() []string { + ret := []string{TextOutput} + ret = append(ret, pf.JSONYamlPrintFlags.AllowedFormats()...) + ret = append(ret, pf.KubeTemplatePrintFlags.AllowedFormats()...) + + return ret +} + +// ToPrinter receives an outputFormat and returns a printer capable of +// handling format printing. +// Returns error if the specified outputFormat does not match supported formats. +func (pf *PrintFlags) ToPrinter() (Printer, error) { + outputFormat := "" + if pf.OutputFormat != nil { + outputFormat = *pf.OutputFormat + } + + if pf.TextPrintFlags != nil { + if p, err := pf.TextPrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) { + return p, err + } + } + + if pf.JSONYamlPrintFlags != nil { + if p, err := pf.JSONYamlPrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) { + return NewResourcePrinterWrapper(pf.TypeSetterPrinter.WrapToPrinter(p, err)) + } + } + + if pf.KubeTemplatePrintFlags != nil { + if p, err := pf.KubeTemplatePrintFlags.ToPrinter(outputFormat); !genericclioptions.IsNoCompatiblePrinterError(err) { + return NewResourcePrinterWrapper(pf.TypeSetterPrinter.WrapToPrinter(p, err)) + } + } + + return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: pf.OutputFormat, AllowedFormats: pf.AllowedFormats()} +} + +// AddFlags receives a *cobra.Command reference and binds +// flags related to Kubeadm printing to it +func (pf *PrintFlags) AddFlags(cmd *cobra.Command) { + pf.JSONYamlPrintFlags.AddFlags(cmd) + pf.KubeTemplatePrintFlags.AddFlags(cmd) + cmd.Flags().StringVarP(pf.OutputFormat, "experimental-output", "o", *pf.OutputFormat, fmt.Sprintf("Output format. One of: %s.", strings.Join(pf.AllowedFormats(), "|"))) +} + +// WithDefaultOutput sets a default output format if one is not provided through a flag value +func (pf *PrintFlags) WithDefaultOutput(outputFormat string) *PrintFlags { + pf.OutputFormat = &outputFormat + return pf +} + +// WithTypeSetter sets a wrapper than will surround the returned printer with a printer to type resources +func (pf *PrintFlags) WithTypeSetter(scheme *runtime.Scheme) *PrintFlags { + pf.TypeSetterPrinter = printers.NewTypeSetter(scheme) + return pf +} + +// NewOutputFlags creates new KubeadmOutputFlags +func NewOutputFlags(textPrintFlags TextPrintFlags) *PrintFlags { + outputFormat := "" + + pf := &PrintFlags{ + OutputFormat: &outputFormat, + + JSONYamlPrintFlags: genericclioptions.NewJSONYamlPrintFlags(), + KubeTemplatePrintFlags: genericclioptions.NewKubeTemplatePrintFlags(), + TextPrintFlags: textPrintFlags, + } + + // disable deprecated --template option + pf.KubeTemplatePrintFlags.TemplateArgument = nil + + return pf +} + +// Printer is a common printing interface in Kubeadm +type Printer interface { + PrintObj(obj runtime.Object, writer io.Writer) error + Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error) + Printf(format string, args ...interface{}) (n int, err error) +} + +// TextPrinter implements Printer interface for generic text output +type TextPrinter struct { +} + +// PrintObj is an implementation of ResourcePrinter.PrintObj that prints object +func (tp *TextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { + _, err := fmt.Fprintf(writer, "%+v\n", obj) + return err +} + +// Fprintf is a wrapper around fmt.Fprintf +func (tp *TextPrinter) Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error) { + return fmt.Fprintf(writer, format, args...) +} + +// Printf is a wrapper around fmt.Printf +func (tp *TextPrinter) Printf(format string, args ...interface{}) (n int, err error) { + return fmt.Printf(format, args...) +} + +// ResourcePrinterWrapper wraps ResourcePrinter and implements Printer interface +type ResourcePrinterWrapper struct { + Printer printers.ResourcePrinter +} + +// NewResourcePrinterWrapper creates new ResourcePrinter object +func NewResourcePrinterWrapper(resourcePrinter printers.ResourcePrinter, err error) (Printer, error) { + if err != nil { + return nil, err + } + return &ResourcePrinterWrapper{Printer: resourcePrinter}, nil +} + +// PrintObj is an implementation of ResourcePrinter.PrintObj that calls underlying printer API +func (rpw *ResourcePrinterWrapper) PrintObj(obj runtime.Object, writer io.Writer) error { + return rpw.Printer.PrintObj(obj, writer) +} + +// Fprintf is an empty method to satisfy Printer interface +// and silent info printing for structured output +// This method is usually redefined for the text output +func (rpw *ResourcePrinterWrapper) Fprintf(writer io.Writer, format string, args ...interface{}) (n int, err error) { + return 0, nil +} + +// Printf is an empty method to satisfy Printer interface +// and silent info printing for structured output +// This method is usually redefined for the text output +func (rpw *ResourcePrinterWrapper) Printf(format string, args ...interface{}) (n int, err error) { + return 0, nil +} From 766e2a45f8d3d3474cf8389e69cef7058de8c9bf Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Tue, 4 Jun 2019 18:19:57 +0300 Subject: [PATCH 3/4] kubeadm token list: implement structured output Used cli-runtime API to print bootstrap tokens in 5 formats: - TEXT (identical to the current output) - YAML - JSON - JSONPATH - Go template --- cmd/kubeadm/app/cmd/BUILD | 5 ++ cmd/kubeadm/app/cmd/token.go | 132 +++++++++++++++++++++++++---------- 2 files changed, 101 insertions(+), 36 deletions(-) diff --git a/cmd/kubeadm/app/cmd/BUILD b/cmd/kubeadm/app/cmd/BUILD index 879e2b1060a..d9ced02bed7 100644 --- a/cmd/kubeadm/app/cmd/BUILD +++ b/cmd/kubeadm/app/cmd/BUILD @@ -19,6 +19,8 @@ go_library( "//cmd/kubeadm/app/apis/kubeadm/scheme:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/v1beta2:go_default_library", "//cmd/kubeadm/app/apis/kubeadm/validation:go_default_library", + "//cmd/kubeadm/app/apis/output/scheme:go_default_library", + "//cmd/kubeadm/app/apis/output/v1alpha1:go_default_library", "//cmd/kubeadm/app/cmd/alpha:go_default_library", "//cmd/kubeadm/app/cmd/options:go_default_library", "//cmd/kubeadm/app/cmd/phases:go_default_library", @@ -41,12 +43,15 @@ go_library( "//cmd/kubeadm/app/util/apiclient:go_default_library", "//cmd/kubeadm/app/util/config:go_default_library", "//cmd/kubeadm/app/util/kubeconfig:go_default_library", + "//cmd/kubeadm/app/util/output:go_default_library", "//cmd/kubeadm/app/util/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library", + "//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/duration:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//staging/src/k8s.io/apimachinery/pkg/version:go_default_library", + "//staging/src/k8s.io/cli-runtime/pkg/genericclioptions:go_default_library", "//staging/src/k8s.io/client-go/kubernetes:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd:go_default_library", "//staging/src/k8s.io/client-go/tools/clientcmd/api:go_default_library", diff --git a/cmd/kubeadm/app/cmd/token.go b/cmd/kubeadm/app/cmd/token.go index 647d8c138fe..cd87235afff 100644 --- a/cmd/kubeadm/app/cmd/token.go +++ b/cmd/kubeadm/app/cmd/token.go @@ -31,7 +31,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/duration" + "k8s.io/cli-runtime/pkg/genericclioptions" clientset "k8s.io/client-go/kubernetes" bootstrapapi "k8s.io/cluster-bootstrap/token/api" bootstraputil "k8s.io/cluster-bootstrap/token/util" @@ -39,6 +41,8 @@ import ( kubeadmscheme "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/scheme" kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation" + outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" + outputapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1" "k8s.io/kubernetes/cmd/kubeadm/app/cmd/options" cmdutil "k8s.io/kubernetes/cmd/kubeadm/app/cmd/util" kubeadmconstants "k8s.io/kubernetes/cmd/kubeadm/app/constants" @@ -46,6 +50,7 @@ import ( "k8s.io/kubernetes/cmd/kubeadm/app/util/apiclient" configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config" kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig" + "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) // NewCmdToken returns cobra.Command for token management @@ -142,6 +147,8 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command { tokenCmd.AddCommand(createCmd) tokenCmd.AddCommand(NewCmdTokenGenerate(out)) + outputFlags := output.NewOutputFlags(&tokenTextPrintFlags{}).WithTypeSetter(outputapischeme.Scheme).WithDefaultOutput(output.TextOutput) + listCmd := &cobra.Command{ Use: "list", Short: "List bootstrap tokens on the server", @@ -155,9 +162,17 @@ func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command { return err } - return RunListTokens(out, errW, client) + printer, err := outputFlags.ToPrinter() + if err != nil { + return err + } + + return RunListTokens(out, errW, client, printer) }, } + + outputFlags.AddFlags(listCmd) + tokenCmd.AddCommand(listCmd) deleteCmd := &cobra.Command{ @@ -268,8 +283,71 @@ func RunGenerateToken(out io.Writer) error { return nil } +func formatBootstrapToken(obj *outputapiv1alpha1.BootstrapToken) string { + ttl := "" + expires := "" + if obj.Expires != nil { + ttl = duration.ShortHumanDuration(obj.Expires.Sub(time.Now())) + expires = obj.Expires.Format(time.RFC3339) + } + ttl = fmt.Sprintf("%-9s", ttl) + + usages := strings.Join(obj.Usages, ",") + if len(usages) == 0 { + usages = "" + } + usages = fmt.Sprintf("%-22s", usages) + + description := obj.Description + if len(description) == 0 { + description = "" + } + description = fmt.Sprintf("%-56s", description) + + groups := strings.Join(obj.Groups, ",") + if len(groups) == 0 { + groups = "" + } + + return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s\n", obj.Token, ttl, expires, usages, description, groups) +} + +// tokenTextPrinter prints bootstrap token in a text form +type tokenTextPrinter struct { + output.TextPrinter + columns []string + headerIsPrinted bool +} + +// PrintObj is an implementation of ResourcePrinter.PrintObj for plain text output +func (ttp *tokenTextPrinter) PrintObj(obj runtime.Object, writer io.Writer) error { + tabw := tabwriter.NewWriter(writer, 10, 4, 3, ' ', 0) + + // Print header + if !ttp.headerIsPrinted { + fmt.Fprintln(tabw, strings.Join(ttp.columns, "\t")) + ttp.headerIsPrinted = true + } + + // Print token + fmt.Fprint(tabw, formatBootstrapToken(obj.(*outputapiv1alpha1.BootstrapToken))) + + return tabw.Flush() +} + +// tokenTextPrintFlags provides flags necessary for printing bootstrap token in a text form. +type tokenTextPrintFlags struct{} + +// ToPrinter returns kubeadm printer for the text output format +func (tpf *tokenTextPrintFlags) ToPrinter(outputFormat string) (output.Printer, error) { + if outputFormat == output.TextOutput { + return &tokenTextPrinter{columns: []string{"TOKEN", "TTL", "EXPIRES", "USAGES", "DESCRIPTION", "EXTRA GROUPS"}}, nil + } + return nil, genericclioptions.NoCompatiblePrinterError{OutputFormat: &outputFormat, AllowedFormats: []string{output.TextOutput}} +} + // RunListTokens lists details on all existing bootstrap tokens on the server. -func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface) error { +func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface, printer output.Printer) error { // First, build our selector for bootstrap tokens only klog.V(1).Infoln("[token] preparing selector for bootstrap token") tokenSelector := fields.SelectorFromSet( @@ -284,16 +362,13 @@ func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface) er FieldSelector: tokenSelector.String(), } - klog.V(1).Infoln("[token] retrieving list of bootstrap tokens") + klog.V(1).Info("[token] retrieving list of bootstrap tokens") secrets, err := client.CoreV1().Secrets(metav1.NamespaceSystem).List(listOptions) if err != nil { return errors.Wrap(err, "failed to list bootstrap tokens") } - w := tabwriter.NewWriter(out, 10, 4, 3, ' ', 0) - fmt.Fprintln(w, "TOKEN\tTTL\tEXPIRES\tUSAGES\tDESCRIPTION\tEXTRA GROUPS") for _, secret := range secrets.Items { - // Get the BootstrapToken struct representation from the Secret object token, err := kubeadmapi.BootstrapTokenFromSecret(&secret) if err != nil { @@ -301,11 +376,22 @@ func RunListTokens(out io.Writer, errW io.Writer, client clientset.Interface) er continue } - // Get the human-friendly string representation for the token - humanFriendlyTokenOutput := humanReadableBootstrapToken(token) - fmt.Fprintln(w, humanFriendlyTokenOutput) + // Convert token into versioned output structure + outputToken := outputapiv1alpha1.BootstrapToken{ + BootstrapToken: kubeadmapiv1beta2.BootstrapToken{ + Token: &kubeadmapiv1beta2.BootstrapTokenString{ID: token.Token.ID, Secret: token.Token.Secret}, + Description: token.Description, + TTL: token.TTL, + Expires: token.Expires, + Usages: token.Usages, + Groups: token.Groups, + }, + } + + if err := printer.PrintObj(&outputToken, out); err != nil { + return errors.Wrapf(err, "unable to print token %s", token.Token) + } } - w.Flush() return nil } @@ -335,32 +421,6 @@ func RunDeleteTokens(out io.Writer, client clientset.Interface, tokenIDsOrTokens return nil } -func humanReadableBootstrapToken(token *kubeadmapi.BootstrapToken) string { - description := token.Description - if len(description) == 0 { - description = "" - } - - ttl := "" - expires := "" - if token.Expires != nil { - ttl = duration.ShortHumanDuration(token.Expires.Sub(time.Now())) - expires = token.Expires.Format(time.RFC3339) - } - - usagesString := strings.Join(token.Usages, ",") - if len(usagesString) == 0 { - usagesString = "" - } - - groupsString := strings.Join(token.Groups, ",") - if len(groupsString) == 0 { - groupsString = "" - } - - return fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%s", token.Token.String(), ttl, expires, usagesString, description, groupsString) -} - func getClientset(file string, dryRun bool) (clientset.Interface, error) { if dryRun { dryRunGetter, err := apiclient.NewClientBackedDryRunGetterFromKubeconfig(file) From ba0c84ade8a94737648173b9ecca4f8882040b40 Mon Sep 17 00:00:00 2001 From: Ed Bartosh Date: Thu, 19 Sep 2019 14:39:54 +0300 Subject: [PATCH 4/4] Test token output Tested JSON, YAML, Go Template and Text token output formats. --- cmd/kubeadm/app/cmd/token_test.go | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/cmd/kubeadm/app/cmd/token_test.go b/cmd/kubeadm/app/cmd/token_test.go index 7fc2d5bd5fd..791c71a4f13 100644 --- a/cmd/kubeadm/app/cmd/token_test.go +++ b/cmd/kubeadm/app/cmd/token_test.go @@ -32,6 +32,9 @@ import ( core "k8s.io/client-go/testing" "k8s.io/client-go/tools/clientcmd" kubeadmapiv1beta2 "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1beta2" + outputapischeme "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/scheme" + outputapiv1alpha1 "k8s.io/kubernetes/cmd/kubeadm/app/apis/output/v1alpha1" + "k8s.io/kubernetes/cmd/kubeadm/app/util/output" ) const ( @@ -332,3 +335,125 @@ func TestRunDeleteTokens(t *testing.T) { t.Errorf("RunDeleteToken() succeeded for an invalid token: %v", err) } } + +func TestTokenOutput(t *testing.T) { + testCases := []struct { + name string + id string + secret string + description string + usages []string + extraGroups []string + outputFormat string + expectedBytes []byte + }{ + { + name: "JSON output", + id: "abcdef", + secret: "1234567890123456", + description: "valid bootstrap tooken", + usages: []string{"signing", "authentication"}, + extraGroups: []string{"system:bootstrappers:kubeadm:default-node-token"}, + outputFormat: "json", + expectedBytes: []byte(`{ + "kind": "BootstrapToken", + "apiVersion": "output.kubeadm.k8s.io/v1alpha1", + "creationTimestamp": null, + "token": "abcdef.1234567890123456", + "description": "valid bootstrap tooken", + "usages": [ + "signing", + "authentication" + ], + "groups": [ + "system:bootstrappers:kubeadm:default-node-token" + ] +} +`), + }, + { + name: "YAML output", + id: "abcdef", + secret: "1234567890123456", + description: "valid bootstrap tooken", + usages: []string{"signing", "authentication"}, + extraGroups: []string{"system:bootstrappers:kubeadm:default-node-token"}, + outputFormat: "yaml", + expectedBytes: []byte(`apiVersion: output.kubeadm.k8s.io/v1alpha1 +creationTimestamp: null +description: valid bootstrap tooken +groups: +- system:bootstrappers:kubeadm:default-node-token +kind: BootstrapToken +token: abcdef.1234567890123456 +usages: +- signing +- authentication +`), + }, + { + name: "Go template output", + id: "abcdef", + secret: "1234567890123456", + description: "valid bootstrap tooken", + usages: []string{"signing", "authentication"}, + extraGroups: []string{"system:bootstrappers:kubeadm:default-node-token"}, + outputFormat: "go-template={{println .token .description .usages .groups}}", + expectedBytes: []byte(`abcdef.1234567890123456 valid bootstrap tooken [signing authentication] [system:bootstrappers:kubeadm:default-node-token] +`), + }, + { + name: "text output", + id: "abcdef", + secret: "1234567890123456", + description: "valid bootstrap tooken", + usages: []string{"signing", "authentication"}, + extraGroups: []string{"system:bootstrappers:kubeadm:default-node-token"}, + outputFormat: "text", + expectedBytes: []byte(`TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS +abcdef.1234567890123456 signing,authentication valid bootstrap tooken system:bootstrappers:kubeadm:default-node-token +`), + }, + { + name: "jsonpath output", + id: "abcdef", + secret: "1234567890123456", + description: "valid bootstrap tooken", + usages: []string{"signing", "authentication"}, + extraGroups: []string{"system:bootstrappers:kubeadm:default-node-token"}, + outputFormat: "jsonpath={.token} {.groups}", + expectedBytes: []byte(`abcdef.1234567890123456 [system:bootstrappers:kubeadm:default-node-token]`), + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + token := outputapiv1alpha1.BootstrapToken{ + BootstrapToken: kubeadmapiv1beta2.BootstrapToken{ + Token: &kubeadmapiv1beta2.BootstrapTokenString{ID: tc.id, Secret: tc.secret}, + Description: tc.description, + Usages: tc.usages, + Groups: tc.extraGroups, + }, + } + buf := bytes.NewBufferString("") + outputFlags := output.NewOutputFlags(&tokenTextPrintFlags{}).WithTypeSetter(outputapischeme.Scheme).WithDefaultOutput(tc.outputFormat) + printer, err := outputFlags.ToPrinter() + if err != nil { + t.Errorf("can't create printer for output format %s: %+v", tc.outputFormat, err) + } + + if err := printer.PrintObj(&token, buf); err != nil { + t.Errorf("unable to print token %s: %+v", token.Token, err) + } + + actualBytes := buf.Bytes() + if !bytes.Equal(actualBytes, tc.expectedBytes) { + t.Errorf( + "failed TestTokenOutput:\n\nexpected:\n%s\n\nactual:\n%s", + string(tc.expectedBytes), + string(actualBytes), + ) + } + }) + } +}