Skip to content

Commit

Permalink
✨ enable capbk to read secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeldeib committed May 22, 2020
1 parent 08e9060 commit 039ea98
Show file tree
Hide file tree
Showing 13 changed files with 569 additions and 70 deletions.
5 changes: 5 additions & 0 deletions bootstrap/kubeadm/api/v1alpha2/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,8 @@ func Convert_v1alpha2_KubeadmConfigSpec_To_v1alpha3_KubeadmConfigSpec(in *Kubead
func Convert_v1alpha3_KubeadmConfigSpec_To_v1alpha2_KubeadmConfigSpec(in *kubeadmbootstrapv1alpha3.KubeadmConfigSpec, out *KubeadmConfigSpec, s apiconversion.Scope) error {
return autoConvert_v1alpha3_KubeadmConfigSpec_To_v1alpha2_KubeadmConfigSpec(in, out, s)
}

func Convert_v1alpha3_File_To_v1alpha2_File(in *kubeadmbootstrapv1alpha3.File, out *File, s apiconversion.Scope) error {
// We don't implement manual conversion for types using contentFrom
return autoConvert_v1alpha3_File_To_v1alpha2_File(in, out, s)
}
40 changes: 28 additions & 12 deletions bootstrap/kubeadm/api/v1alpha2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 25 additions & 1 deletion bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,31 @@ type File struct {
Encoding Encoding `json:"encoding,omitempty"`

// Content is the actual content of the file.
Content string `json:"content"`
Content string `json:"content,omitempty"`

// ContentFrom is a referenced source of content to populate the file.
ContentFrom *FileSource `json:"contentFrom,omitempty"`
}

// FileSource is a union of all possible external source types for file data.
// Only one field may be populated in any given instance. Developers adding new
// sources of data for target systems should add them here.
type FileSource struct {
// Secret represents a secret that should populate this file.
// +optional
Secret *SecretFileSource `json:"secret,omitempty"`
}

// Adapts a Secret into a FileSource.
//
// The contents of the target Secret's Data field will be presented
// as files using the keys in the Data field as the file names.
type SecretFileSource struct {
// Name of the secret in the KubeadmBootstrapConfig's namespace to use.
Name string `json:"name"`

// Key is the key in the secret's data map for this value.
Key string `json:"key"`
}

// User defines the input for a generated user in cloud-init.
Expand Down
157 changes: 111 additions & 46 deletions bootstrap/kubeadm/api/v1alpha3/kubeadmbootstrapconfig_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,62 +17,127 @@ limitations under the License.
package v1alpha3

import (
. "github.com/onsi/ginkgo"
"testing"

. "github.com/onsi/gomega"

"golang.org/x/net/context"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
)

// These tests are written in BDD-style using Ginkgo framework. Refer to
// http://onsi.github.io/ginkgo to learn more.

var _ = Describe("KubeadmConfig", func() {
var (
key types.NamespacedName
created, fetched *KubeadmConfig
)

BeforeEach(func() {
// Add any setup steps that needs to be executed before each test
})

AfterEach(func() {
// Add any teardown steps that needs to be executed after each test
})

// Add Tests for OpenAPI validation (or additional CRD features) specified in
// your API definition.
// Avoid adding tests for vanilla CRUD operations because they would
// test Kubernetes API server, which isn't the goal here.
Context("Create API", func() {

It("should create an object successfully", func() {

key = types.NamespacedName{
Name: "foo",
Namespace: "default",
}
created = &KubeadmConfig{
func TestClusterValidate(t *testing.T) {
cases := map[string]struct {
in *KubeadmConfig
expectErr bool
}{
"valid content": {
in: &KubeadmConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: "default",
},
Spec: KubeadmConfigSpec{
Files: []File{
{
Content: "foo",
},
},
},
},
},
"valid contentFrom": {
in: &KubeadmConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: "default",
},
Spec: KubeadmConfigSpec{
Files: []File{
{
ContentFrom: &FileSource{
Secret: &SecretFileSource{
Name: "foo",
Key: "bar",
},
},
},
},
},
},
},
"invalid content and contentFrom": {
in: &KubeadmConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Name: "baz",
Namespace: "default",
},
Spec: KubeadmConfigSpec{
Files: []File{
{
ContentFrom: &FileSource{},
Content: "foo",
},
},
},
},
expectErr: true,
},
"invalid contentFrom without name": {
in: &KubeadmConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: "default",
},
Spec: KubeadmConfigSpec{
Files: []File{
{
ContentFrom: &FileSource{
Secret: &SecretFileSource{
Key: "bar",
},
},
Content: "foo",
},
},
},
},
expectErr: true,
},
"invalid contentFrom without key": {
in: &KubeadmConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "baz",
Namespace: "default",
},
Spec: KubeadmConfigSpec{
Files: []File{
{
ContentFrom: &FileSource{
Secret: &SecretFileSource{
Name: "foo",
},
},
Content: "foo",
},
},
},
},
expectErr: true,
},
}

for name, tt := range cases {
t.Run(name, func(t *testing.T) {
g := NewWithT(t)
if tt.expectErr {
g.Expect(tt.in.ValidateCreate()).NotTo(Succeed())
g.Expect(tt.in.ValidateUpdate(nil)).NotTo(Succeed())
} else {
g.Expect(tt.in.ValidateCreate()).To(Succeed())
g.Expect(tt.in.ValidateUpdate(nil)).To(Succeed())
}

By("creating an API obj")
Expect(k8sClient.Create(context.TODO(), created)).To(Succeed())

fetched = &KubeadmConfig{}
Expect(k8sClient.Get(context.TODO(), key, fetched)).To(Succeed())
Expect(fetched).To(Equal(created))

By("deleting the created object")
Expect(k8sClient.Delete(context.TODO(), created)).To(Succeed())
Expect(k8sClient.Get(context.TODO(), key, created)).ToNot(Succeed())
})

})

})
}
}
91 changes: 89 additions & 2 deletions bootstrap/kubeadm/api/v1alpha3/kubeadmconfig_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,98 @@ limitations under the License.
package v1alpha3

import (
"fmt"

apierrors "k8s.io/apimachinery/pkg/api/errors"
runtime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook"
)

var (
ConflictingFileSourceMsg = "only one of content of contentFrom may be specified for a single file"
MissingFileSourceMsg = "source for file content must be specified if contenFrom is non-nil"
MissingSecretNameMsg = "secret file source must specify non-empty secret name"
MissingSecretKeyMsg = "secret file source must specify non-empty secret key"
)

func (r *KubeadmConfig) SetupWebhookWithManager(mgr ctrl.Manager) error {
func (c *KubeadmConfig) SetupWebhookWithManager(mgr ctrl.Manager) error {
return ctrl.NewWebhookManagedBy(mgr).
For(r).
For(c).
Complete()
}

// +kubebuilder:webhook:verbs=create;update,path=/validate-bootstrap-x-k8s-io-v1alpha3-kubeadmconfig,mutating=false,failurePolicy=fail,matchPolicy=Equivalent,groups=bootstrap.cluster.x-k8s.io,resources=kubeadmconfigs,versions=v1alpha3,name=validation.bootstrap.cluster.x-k8s.io,sideEffects=None

var _ webhook.Validator = &KubeadmConfig{}

// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
func (c *KubeadmConfig) ValidateCreate() error {
return c.validate()
}

// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
func (c *KubeadmConfig) ValidateUpdate(old runtime.Object) error {
return c.validate()
}

// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
func (c *KubeadmConfig) ValidateDelete() error {
return nil
}

func (c *KubeadmConfig) validate() error {
var allErrs field.ErrorList

for i := range c.Spec.Files {
file := c.Spec.Files[i]
if file.Content != "" && file.ContentFrom != nil {
allErrs = append(
allErrs,
field.Invalid(
field.NewPath("spec", "files", fmt.Sprintf("%d", i)),
file,
ConflictingFileSourceMsg,
),
)
}
if file.ContentFrom != nil && file.ContentFrom.Secret == nil {
allErrs = append(
allErrs,
field.Invalid(
field.NewPath("spec", "files", fmt.Sprintf("%d", i), "contentFrom", "secret"),
file,
MissingFileSourceMsg,
),
)
}
if file.ContentFrom != nil && file.ContentFrom.Secret != nil {
if file.ContentFrom.Secret.Name == "" {
allErrs = append(
allErrs,
field.Invalid(
field.NewPath("spec", "files", fmt.Sprintf("%d", i), "contentFrom", "secret", "name"),
file,
MissingSecretNameMsg,
),
)
}
if file.ContentFrom.Secret.Key == "" {
allErrs = append(
allErrs,
field.Invalid(
field.NewPath("spec", "files", fmt.Sprintf("%d", i), "contentFrom", "secret", "key"),
file,
MissingSecretKeyMsg,
),
)
}
}
}

if len(allErrs) == 0 {
return nil
}
return apierrors.NewInvalid(GroupVersion.WithKind("KubeadmConfig").GroupKind(), c.Name, allErrs)
}
Loading

0 comments on commit 039ea98

Please sign in to comment.