mirror of https://github.com/kubernetes/kops.git
255 lines
6.7 KiB
Go
255 lines
6.7 KiB
Go
/*
|
|
Copyright 2018 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.
|
|
*/
|
|
|
|
/*
|
|
Copyright 2018 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 tests
|
|
|
|
import (
|
|
"math/big"
|
|
"reflect"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"k8s.io/kops/pkg/apis/kops"
|
|
"k8s.io/kops/pkg/diff"
|
|
"k8s.io/kops/upup/pkg/fi"
|
|
"k8s.io/kops/upup/pkg/fi/fitasks"
|
|
"k8s.io/kops/util/pkg/vfs"
|
|
)
|
|
|
|
type MockTarget struct {
|
|
}
|
|
|
|
func (t *MockTarget) Finish(taskMap map[string]fi.Task) error {
|
|
return nil
|
|
}
|
|
|
|
func (t *MockTarget) ProcessDeletions() bool {
|
|
return false
|
|
}
|
|
|
|
var _ fi.Target = &MockTarget{}
|
|
|
|
// Verifies that we regenerate keyset.yaml if they are deleted, which covers the upgrade scenario from kops 1.8 -> kops 1.9
|
|
func TestKeypairUpgrade(t *testing.T) {
|
|
lifecycle := fi.LifecycleSync
|
|
|
|
runTasksOptions := fi.RunTasksOptions{}
|
|
runTasksOptions.MaxTaskDuration = 2 * time.Second
|
|
|
|
target := &MockTarget{}
|
|
|
|
cluster := &kops.Cluster{}
|
|
vfs.Context.ResetMemfsContext(true)
|
|
|
|
basedir, err := vfs.Context.BuildVfsPath("memfs://keystore")
|
|
if err != nil {
|
|
t.Fatalf("error building vfs path: %v", err)
|
|
}
|
|
|
|
keystore := fi.NewVFSCAStore(cluster, basedir, true)
|
|
|
|
// Generate predictable sequence numbers for testing
|
|
var n int64
|
|
keystore.SerialGenerator = func() *big.Int {
|
|
n++
|
|
return big.NewInt(n)
|
|
}
|
|
|
|
// We define a function so we can rebuild the tasks, because we modify in-place when running
|
|
buildTasks := func() map[string]fi.Task {
|
|
format := string(fi.KeysetFormatV1Alpha2)
|
|
|
|
ca := &fitasks.Keypair{
|
|
Name: fi.String(fi.CertificateId_CA),
|
|
Lifecycle: &lifecycle,
|
|
Subject: "cn=kubernetes",
|
|
Type: "ca",
|
|
Format: format,
|
|
}
|
|
|
|
kubelet := &fitasks.Keypair{
|
|
Name: fi.String("kubelet"),
|
|
Lifecycle: &lifecycle,
|
|
Subject: "o=nodes,cn=kubelet",
|
|
Type: "client",
|
|
Signer: ca,
|
|
Format: format,
|
|
}
|
|
|
|
tasks := make(map[string]fi.Task)
|
|
tasks["ca"] = ca
|
|
tasks["kubelet"] = kubelet
|
|
return tasks
|
|
}
|
|
|
|
t.Logf("Building some keypairs")
|
|
{
|
|
allTasks := buildTasks()
|
|
|
|
context, err := fi.NewContext(target, nil, nil, keystore, nil, nil, true, allTasks)
|
|
if err != nil {
|
|
t.Fatalf("error building context: %v", err)
|
|
}
|
|
|
|
if err := context.RunTasks(runTasksOptions); err != nil {
|
|
t.Fatalf("unexpected error during Run: %v", err)
|
|
}
|
|
}
|
|
|
|
// Check that the expected files were generated
|
|
expected := []string{
|
|
"memfs://keystore/issued/ca/1.crt",
|
|
"memfs://keystore/issued/ca/keyset.yaml",
|
|
"memfs://keystore/issued/kubelet/2.crt",
|
|
"memfs://keystore/issued/kubelet/keyset.yaml",
|
|
"memfs://keystore/private/ca/1.key",
|
|
"memfs://keystore/private/ca/keyset.yaml",
|
|
"memfs://keystore/private/kubelet/2.key",
|
|
"memfs://keystore/private/kubelet/keyset.yaml",
|
|
}
|
|
checkPaths(t, basedir, expected)
|
|
|
|
// Save the contents of those files
|
|
contents := make(map[string]string)
|
|
for _, k := range expected {
|
|
p, err := vfs.Context.BuildVfsPath(k)
|
|
if err != nil {
|
|
t.Fatalf("error building vfs path: %v", err)
|
|
}
|
|
b, err := p.ReadFile()
|
|
if err != nil {
|
|
t.Fatalf("error reading vfs path: %v", err)
|
|
}
|
|
contents[k] = string(b)
|
|
}
|
|
|
|
t.Logf("verifying that rerunning tasks does not change keys")
|
|
{
|
|
allTasks := buildTasks()
|
|
|
|
context, err := fi.NewContext(target, nil, nil, keystore, nil, nil, true, allTasks)
|
|
if err != nil {
|
|
t.Fatalf("error building context: %v", err)
|
|
}
|
|
|
|
if err := context.RunTasks(runTasksOptions); err != nil {
|
|
t.Fatalf("unexpected error during Run: %v", err)
|
|
}
|
|
}
|
|
checkContents(t, basedir, contents)
|
|
|
|
t.Logf("deleting keyset.yaml files and verifying they are recreated")
|
|
FailOnError(t, basedir.Join("issued/ca/keyset.yaml").Remove())
|
|
FailOnError(t, basedir.Join("issued/kubelet/keyset.yaml").Remove())
|
|
FailOnError(t, basedir.Join("private/ca/keyset.yaml").Remove())
|
|
FailOnError(t, basedir.Join("private/kubelet/keyset.yaml").Remove())
|
|
|
|
{
|
|
allTasks := buildTasks()
|
|
|
|
context, err := fi.NewContext(target, nil, nil, keystore, nil, nil, true, allTasks)
|
|
if err != nil {
|
|
t.Fatalf("error building context: %v", err)
|
|
}
|
|
|
|
if err := context.RunTasks(runTasksOptions); err != nil {
|
|
t.Fatalf("unexpected error during Run: %v", err)
|
|
}
|
|
}
|
|
checkContents(t, basedir, contents)
|
|
}
|
|
|
|
// FailOnError calls t.Fatalf if err != nil
|
|
func FailOnError(t *testing.T, err error) {
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
// checkPaths verifies that the path names in the tree rooted at basedir are exactly as expected
|
|
// Unlike checkContents, it only verifies the names, not the contents
|
|
func checkPaths(t *testing.T, basedir vfs.Path, expected []string) {
|
|
paths, err := basedir.ReadTree()
|
|
if err != nil {
|
|
t.Errorf("ReadTree failed: %v", err)
|
|
}
|
|
|
|
var actual []string
|
|
for _, p := range paths {
|
|
actual = append(actual, p.Path())
|
|
}
|
|
sort.Strings(actual)
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
t.Fatalf("unexpected paths: %v", actual)
|
|
}
|
|
}
|
|
|
|
// checkPaths verifies that the files and their contents in the tree rooted at basedir are exactly as expected
|
|
func checkContents(t *testing.T, basedir vfs.Path, expected map[string]string) {
|
|
paths, err := basedir.ReadTree()
|
|
if err != nil {
|
|
t.Errorf("ReadTree failed: %v", err)
|
|
}
|
|
|
|
actual := make(map[string]string)
|
|
for _, p := range paths {
|
|
b, err := p.ReadFile()
|
|
if err != nil {
|
|
t.Fatalf("error reading vfs path %q: %v", p, err)
|
|
}
|
|
actual[p.Path()] = string(b)
|
|
}
|
|
|
|
var actualKeys []string
|
|
for k := range actual {
|
|
actualKeys = append(actualKeys, k)
|
|
}
|
|
sort.Strings(actualKeys)
|
|
var expectedKeys []string
|
|
for k := range expected {
|
|
expectedKeys = append(expectedKeys, k)
|
|
}
|
|
sort.Strings(expectedKeys)
|
|
|
|
if !reflect.DeepEqual(actualKeys, expectedKeys) {
|
|
t.Fatalf("unexpected paths: %v", actualKeys)
|
|
}
|
|
if !reflect.DeepEqual(actual, expected) {
|
|
for k := range actual {
|
|
if actual[k] != expected[k] {
|
|
t.Errorf("mismatch on key %q", k)
|
|
t.Errorf("diff: %s", diff.FormatDiff(actual[k], expected[k]))
|
|
}
|
|
}
|
|
}
|
|
}
|