Merge pull request #54 from justinsb/fix_tests

Update & get tests working again
This commit is contained in:
Justin Santa Barbara 2016-07-06 03:06:59 -04:00 committed by GitHub
commit fa3157e512
7 changed files with 350 additions and 115 deletions

View File

@ -14,7 +14,6 @@ codegen:
GO15VENDOREXPERIMENT=1 go generate k8s.io/kops/upup/pkg/fi/fitasks
test:
GO15VENDOREXPERIMENT=1 go test k8s.io/kops/cmd/...
GO15VENDOREXPERIMENT=1 go test k8s.io/kops/upup/pkg/...
godeps:

View File

@ -1,111 +0,0 @@
package main
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/fi/vfs"
"testing"
)
// TODO: Refactor CreateClusterCmd into pkg/fi/cloudup
func buildDefaultCreateCluster() *CreateClusterCmd {
var err error
c := &CreateClusterCmd{}
c.ClusterConfig = &cloudup.CloudConfig{}
c.ClusterConfig.ClusterName = "testcluster.mydomain.com"
c.ClusterConfig.NodeZones = []string{"us-east-1a", "us-east-1b", "us-east-1c"}
c.ClusterConfig.MasterZones = c.ClusterConfig.NodeZones
c.SSHPublicKey = "~/.ssh/id_rsa.pub"
c.ClusterConfig.CloudProvider = "aws"
dryrun := false
c.StateStore, err = fi.NewVFSStateStore(vfs.NewFSPath("test-state"), dryrun)
if err != nil {
glog.Fatalf("error building state store: %v", err)
}
return c
}
func expectErrorFromRun(t *testing.T, c *CreateClusterCmd, message string) {
err := c.Run()
if err == nil {
t.Fatalf("Expected error from run")
}
actualMessage := fmt.Sprintf("%v", err)
if actualMessage != message {
t.Fatalf("Expected error %q, got %q", message, actualMessage)
}
}
func TestCreateCluster_DuplicateZones(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{"us-east-1a", "us-east-1b", "us-east-1b"}
c.ClusterConfig.MasterZones = []string{"us-east-1a"}
expectErrorFromRun(t, c, "NodeZones contained a duplicate value: us-east-1b")
}
func TestCreateCluster_NoClusterName(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.ClusterName = ""
expectErrorFromRun(t, c, "-name is required (e.g. mycluster.myzone.com)")
}
func TestCreateCluster_NoCloud(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.CloudProvider = ""
expectErrorFromRun(t, c, "-cloud is required (e.g. aws, gce)")
}
func TestCreateCluster_ExtraMasterZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{"us-east-1a", "us-east-1c"}
c.ClusterConfig.MasterZones = []string{"us-east-1a", "us-east-1b", "us-east-1c"}
expectErrorFromRun(t, c, "All MasterZones must (currently) also be NodeZones")
}
func TestCreateCluster_NoMasterZones(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.MasterZones = []string{}
expectErrorFromRun(t, c, "must specify at least one MasterZone")
}
func TestCreateCluster_NoNodeZones(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{}
expectErrorFromRun(t, c, "must specify at least one NodeZone")
}
func TestCreateCluster_RegionAsZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{"us-east-1"}
c.ClusterConfig.MasterZones = c.ClusterConfig.NodeZones
expectErrorFromRun(t, c, "Region is not a recognized EC2 region: \"us-east-\" (check you have specified valid zones?)")
}
func TestCreateCluster_BadZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{"us-east-1z"}
c.ClusterConfig.MasterZones = c.ClusterConfig.NodeZones
expectErrorFromRun(t, c, "Zone is not a recognized AZ: \"us-east-1z\" (check you have specified a valid zone?)")
}
func TestCreateCluster_MixedRegion(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{"us-west-1a", "us-west-2b", "us-west-2c"}
c.ClusterConfig.MasterZones = c.ClusterConfig.NodeZones
expectErrorFromRun(t, c, "Clusters cannot span multiple regions")
}
func TestCreateCluster_EvenEtcdClusterSize(t *testing.T) {
c := buildDefaultCreateCluster()
c.ClusterConfig.NodeZones = []string{"us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"}
c.ClusterConfig.MasterZones = c.ClusterConfig.NodeZones
expectErrorFromRun(t, c, "There should be an odd number of master-zones, for etcd's quorum. Hint: Use -zones and -master-zones to declare node zones and master zones separately.")
}

View File

@ -138,7 +138,7 @@ func (c *Cluster) Validate() error {
return fmt.Errorf("MasterKubelet CloudProvider did not match cluster CloudProvider")
}
if c.Spec.KubeAPIServer.CloudProvider != c.Spec.CloudProvider {
return fmt.Errorf("Errorf CloudProvider did not match cluster CloudProvider")
return fmt.Errorf("KubeAPIServer CloudProvider did not match cluster CloudProvider")
}
if c.Spec.KubeControllerManager.CloudProvider != c.Spec.CloudProvider {
return fmt.Errorf("KubeControllerManager CloudProvider did not match cluster CloudProvider")

View File

@ -112,7 +112,7 @@ func (c *CreateClusterCmd) Run() error {
if len(c.Cluster.Spec.Zones) == 0 {
// TODO: Auto choose zones from region?
return fmt.Errorf("must configuration at least one Zone (use --zones)")
return fmt.Errorf("must configure at least one Zone (use --zones)")
}
if len(c.InstanceGroups) == 0 {
@ -208,7 +208,7 @@ func (c *CreateClusterCmd) Run() error {
if (len(etcdZones) % 2) == 0 {
// Not technically a requirement, but doesn't really make sense to allow
return fmt.Errorf("There should be an odd number of master-zones, for etcd's quorum. Hint: Use --zone and --master-zone to declare node zones and master zones separately.")
return fmt.Errorf("There should be an odd number of master-zones, for etcd's quorum. Hint: Use --zones and --master-zones to declare node zones and master zones separately.")
}
}
}

View File

@ -0,0 +1,212 @@
package cloudup
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kops/upup/pkg/api"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/vfs"
k8sapi "k8s.io/kubernetes/pkg/api"
"os"
"path"
"strings"
"testing"
)
func buildDefaultCreateCluster() *CreateClusterCmd {
var err error
memfs := vfs.NewMemFSContext()
memfs.MarkClusterReadable()
c := &CreateClusterCmd{}
// TODO: We should actually just specify the minimums here, and run in though the default logic
c.Cluster = &api.Cluster{}
c.Cluster.Name = "testcluster.mydomain.com"
c.Cluster.Spec.Zones = []*api.ClusterZoneSpec{
{Name: "us-east-1a", CIDR: "172.20.1.0/24"},
{Name: "us-east-1b", CIDR: "172.20.2.0/24"},
{Name: "us-east-1c", CIDR: "172.20.3.0/24"},
{Name: "us-east-1d", CIDR: "172.20.4.0/24"},
}
c.InstanceGroups = append(c.InstanceGroups, buildNodeInstanceGroup("us-east-1a"))
c.InstanceGroups = append(c.InstanceGroups, buildMasterInstanceGroup("us-east-1a"))
c.SSHPublicKey = path.Join(os.Getenv("HOME"), ".ssh", "id_rsa.pub")
c.Cluster.Spec.Kubelet = &api.KubeletConfig{}
c.Cluster.Spec.KubeControllerManager = &api.KubeControllerManagerConfig{}
c.Cluster.Spec.KubeDNS = &api.KubeDNSConfig{}
c.Cluster.Spec.KubeAPIServer = &api.KubeAPIServerConfig{}
c.Cluster.Spec.KubeProxy = &api.KubeProxyConfig{}
c.Cluster.Spec.Docker = &api.DockerConfig{}
c.Cluster.Spec.NetworkCIDR = "172.20.0.0/16"
c.Cluster.Spec.NonMasqueradeCIDR = "100.64.0.0/10"
c.Cluster.Spec.Kubelet.NonMasqueradeCIDR = c.Cluster.Spec.NonMasqueradeCIDR
c.Cluster.Spec.ServiceClusterIPRange = "100.64.1.0/24"
c.Cluster.Spec.KubeAPIServer.ServiceClusterIPRange = c.Cluster.Spec.ServiceClusterIPRange
c.Cluster.Spec.KubeDNS.ServerIP = "100.64.1.10"
c.Cluster.Spec.Kubelet.ClusterDNS = c.Cluster.Spec.KubeDNS.ServerIP
c.Cluster.Spec.CloudProvider = "aws"
c.Cluster.Spec.Kubelet.CloudProvider = c.Cluster.Spec.CloudProvider
c.Cluster.Spec.KubeAPIServer.CloudProvider = c.Cluster.Spec.CloudProvider
c.Cluster.Spec.KubeControllerManager.CloudProvider = c.Cluster.Spec.CloudProvider
c.Target = "dryrun"
dryrun := false
c.StateStore, err = fi.NewVFSStateStore(vfs.NewMemFSPath(memfs, "test-statestore"), c.Cluster.Name, dryrun)
if err != nil {
glog.Fatalf("error building state store: %v", err)
}
return c
}
func buildNodeInstanceGroup(zones ...string) *api.InstanceGroup {
g := &api.InstanceGroup{
ObjectMeta: k8sapi.ObjectMeta{Name: "nodes-" + strings.Join(zones, "-")},
Spec: api.InstanceGroupSpec{
Role: api.InstanceGroupRoleNode,
Zones: zones,
},
}
return g
}
func buildMasterInstanceGroup(zones ...string) *api.InstanceGroup {
g := &api.InstanceGroup{
ObjectMeta: k8sapi.ObjectMeta{Name: "master-" + strings.Join(zones, "-")},
Spec: api.InstanceGroupSpec{
Role: api.InstanceGroupRoleMaster,
Zones: zones,
},
}
return g
}
func expectErrorFromRun(t *testing.T, c *CreateClusterCmd, message string) {
err := c.Run()
if err == nil {
t.Fatalf("Expected error from run")
}
actualMessage := fmt.Sprintf("%v", err)
if !strings.Contains(actualMessage, message) {
t.Fatalf("Expected error %q, got %q", message, actualMessage)
}
}
func TestCreateCluster_DuplicateZones(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Spec.Zones = []*api.ClusterZoneSpec{
{Name: "us-east-1a", CIDR: "172.20.1.0/24"},
{Name: "us-east-1a", CIDR: "172.20.2.0/24"},
}
expectErrorFromRun(t, c, "Zones contained a duplicate value: us-east-1a")
}
func TestCreateCluster_NoClusterName(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Name = ""
expectErrorFromRun(t, c, "ClusterName is required")
}
func TestCreateCluster_NoCloud(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Spec.CloudProvider = ""
expectErrorFromRun(t, c, "-cloud is required (e.g. aws, gce)")
}
func TestCreateCluster_ExtraMasterZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Spec.Zones = []*api.ClusterZoneSpec{
{Name: "us-east-1a", CIDR: "172.20.1.0/24"},
{Name: "us-east-1b", CIDR: "172.20.2.0/24"},
}
c.InstanceGroups = []*api.InstanceGroup{}
c.InstanceGroups = append(c.InstanceGroups, buildNodeInstanceGroup("us-east-1a", "us-east-1b"))
c.InstanceGroups = append(c.InstanceGroups, buildMasterInstanceGroup("us-east-1a", "us-east-1b", "us-east-1c"))
expectErrorFromRun(t, c, "is not configured as a Zone in the cluster")
}
func TestCreateCluster_NoMasterZones(t *testing.T) {
c := buildDefaultCreateCluster()
c.InstanceGroups = []*api.InstanceGroup{}
c.InstanceGroups = append(c.InstanceGroups, buildNodeInstanceGroup("us-east-1a"))
expectErrorFromRun(t, c, "must configure at least one Master InstanceGroup")
}
func TestCreateCluster_NoNodeZones(t *testing.T) {
c := buildDefaultCreateCluster()
c.InstanceGroups = []*api.InstanceGroup{}
c.InstanceGroups = append(c.InstanceGroups, buildMasterInstanceGroup("us-east-1a"))
expectErrorFromRun(t, c, "must configure at least one Node InstanceGroup")
}
func TestCreateCluster_RegionAsZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Spec.Zones = []*api.ClusterZoneSpec{
{Name: "us-east-1", CIDR: "172.20.1.0/24"},
}
c.InstanceGroups = []*api.InstanceGroup{
buildNodeInstanceGroup("us-east-1"),
buildMasterInstanceGroup("us-east-1"),
}
expectErrorFromRun(t, c, "Region is not a recognized EC2 region: \"us-east-\" (check you have specified valid zones?)")
}
func TestCreateCluster_NotIncludedZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.InstanceGroups = []*api.InstanceGroup{
buildNodeInstanceGroup("us-east-1e"),
buildMasterInstanceGroup("us-east-1a"),
}
expectErrorFromRun(t, c, "not configured as a Zone in the cluster")
}
func TestCreateCluster_BadZone(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Spec.Zones = []*api.ClusterZoneSpec{
{Name: "us-east-1z", CIDR: "172.20.1.0/24"},
}
c.InstanceGroups = []*api.InstanceGroup{
buildNodeInstanceGroup("us-east-1z"),
buildMasterInstanceGroup("us-east-1z"),
}
expectErrorFromRun(t, c, "Zone is not a recognized AZ: \"us-east-1z\" (check you have specified a valid zone?)")
}
func TestCreateCluster_MixedRegion(t *testing.T) {
c := buildDefaultCreateCluster()
c.Cluster.Spec.Zones = []*api.ClusterZoneSpec{
{Name: "us-east-1a", CIDR: "172.20.1.0/24"},
{Name: "us-west-1b", CIDR: "172.20.2.0/24"},
}
c.InstanceGroups = []*api.InstanceGroup{
buildNodeInstanceGroup("us-east-1a", "us-west-1b"),
buildMasterInstanceGroup("us-east-1a"),
}
expectErrorFromRun(t, c, "Clusters cannot span multiple regions")
}
func TestCreateCluster_EvenEtcdClusterSize(t *testing.T) {
c := buildDefaultCreateCluster()
c.InstanceGroups = []*api.InstanceGroup{}
c.InstanceGroups = append(c.InstanceGroups, buildNodeInstanceGroup("us-east-1a"))
c.InstanceGroups = append(c.InstanceGroups, buildMasterInstanceGroup("us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"))
c.Cluster.Spec.EtcdClusters = []*api.EtcdClusterSpec{
{
Name: "main",
Members: []*api.EtcdMemberSpec{
{Name: "us-east-1a", Zone: "us-east-1a"},
{Name: "us-east-1b", Zone: "us-east-1b"},
},
},
}
expectErrorFromRun(t, c, "There should be an odd number of master-zones, for etcd's quorum. Hint: Use --zones and --master-zones to declare node zones and master zones separately.")
}

127
upup/pkg/fi/vfs/memfs.go Normal file
View File

@ -0,0 +1,127 @@
package vfs
import (
"os"
"path"
"strings"
)
type MemFSPath struct {
context *MemFSContext
location string
contents []byte
children map[string]*MemFSPath
}
var _ Path = &MemFSPath{}
type MemFSContext struct {
clusterReadable bool
root *MemFSPath
}
func NewMemFSContext() *MemFSContext {
c := &MemFSContext{}
c.root = &MemFSPath{
context: c,
location: "",
}
return c
}
// MarkClusterReadable pretends the current memfscontext is cluster readable; this is useful for tests
func (c *MemFSContext) MarkClusterReadable() {
c.clusterReadable = true
}
func (c *MemFSPath) IsClusterReadable() bool {
return c.context.clusterReadable
}
var _ HasClusterReadable = &MemFSPath{}
func NewMemFSPath(context *MemFSContext, location string) *MemFSPath {
return context.root.Join(location).(*MemFSPath)
}
func (p *MemFSPath) Join(relativePath ...string) Path {
joined := path.Join(relativePath...)
tokens := strings.Split(joined, "/")
current := p
for _, token := range tokens {
if current.children == nil {
current.children = make(map[string]*MemFSPath)
}
child := current.children[token]
if child == nil {
child = &MemFSPath{
context: p.context,
location: path.Join(current.location, token),
}
current.children[token] = child
}
current = child
}
return current
}
func (p *MemFSPath) WriteFile(data []byte) error {
p.contents = data
return nil
}
func (p *MemFSPath) CreateFile(data []byte) error {
// Check if exists
if p.contents != nil {
return os.ErrExist
}
return p.WriteFile(data)
}
func (p *MemFSPath) ReadFile() ([]byte, error) {
if p.contents == nil {
return nil, os.ErrNotExist
}
// TODO: Copy?
return p.contents, nil
}
func (p *MemFSPath) ReadDir() ([]Path, error) {
var paths []Path
for _, f := range p.children {
paths = append(paths, f)
}
return paths, nil
}
func (p *MemFSPath) ReadTree() ([]Path, error) {
var paths []Path
p.readTree(&paths)
return paths, nil
}
func (p *MemFSPath) readTree(dest *[]Path) {
for _, f := range p.children {
*dest = append(*dest, f)
f.readTree(dest)
}
}
func (p *MemFSPath) Base() string {
return path.Base(p.location)
}
func (p *MemFSPath) Path() string {
return p.location
}
func (p *MemFSPath) String() string {
return p.Path()
}
func (p *MemFSPath) Remove() error {
p.contents = nil
return nil
}

View File

@ -61,6 +61,10 @@ func RelativePath(base Path, child Path) (string, error) {
}
func IsClusterReadable(p Path) bool {
if hcr, ok := p.(HasClusterReadable); ok {
return hcr.IsClusterReadable()
}
switch p.(type) {
case *S3Path:
return true
@ -76,3 +80,7 @@ func IsClusterReadable(p Path) bool {
return false
}
}
type HasClusterReadable interface {
IsClusterReadable() bool
}