Merge branch 'master' into kris-and-eric-1282

This commit is contained in:
Eric Hole 2017-01-08 11:10:13 -05:00 committed by GitHub
commit 0f84494dbd
5004 changed files with 183555 additions and 423393 deletions

3
.dockerignore Normal file
View File

@ -0,0 +1,3 @@
*
!.build
!protokube

View File

@ -30,11 +30,9 @@ MAKEDIR:=$(strip $(shell dirname "$(realpath $(lastword $(MAKEFILE_LIST)))"))
# Keep in sync with upup/models/cloudup/resources/addons/dns-controller/
DNS_CONTROLLER_TAG=1.4.1
PROTOKUBE_TAG=1.5.0
ifndef VERSION
VERSION := git-$(shell git describe --always)
endif
VERSION?=1.5.0-alpha1
# Go exports:
@ -49,7 +47,7 @@ ifdef STATIC_BUILD
endif
kops: kops-gobindata
go install ${EXTRA_BUILDFLAGS} -ldflags "-X main.BuildVersion=${VERSION} ${EXTRA_LDFLAGS}" k8s.io/kops/cmd/kops/...
go install ${EXTRA_BUILDFLAGS} -ldflags "-X k8s.io/kops.Version=${VERSION} ${EXTRA_LDFLAGS}" k8s.io/kops/cmd/kops/...
gobindata-tool:
go build ${EXTRA_BUILDFLAGS} -ldflags "${EXTRA_LDFLAGS}" -o ${GOPATH_1ST}/bin/go-bindata k8s.io/kops/vendor/github.com/jteeuwen/go-bindata/go-bindata
@ -81,12 +79,11 @@ test:
go test k8s.io/kops/protokube/... -args -v=1 -logtostderr
go test k8s.io/kops/dns-controller/pkg/... -args -v=1 -logtostderr
go test k8s.io/kops/cmd/... -args -v=1 -logtostderr
go test k8s.io/kops/tests/... -args -v=1 -logtostderr
crossbuild-nodeup:
mkdir -p .build/dist/
#GOOS=darwin GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/darwin/amd64/nodeup -ldflags "${EXTRA_LDFLAGS} -X main.BuildVersion=${VERSION}" k8s.io/kops/cmd/nodeup
GOOS=linux GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/linux/amd64/nodeup -ldflags "${EXTRA_LDFLAGS} -X main.BuildVersion=${VERSION}" k8s.io/kops/cmd/nodeup
#GOOS=windows GOARCH=amd64 go build -o .build/dist/windows/amd64/kops -ldflags "-X main.BuildVersion=${VERSION}" -v k8s.io/kops/cmd/kops/...
GOOS=linux GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/linux/amd64/nodeup -ldflags "${EXTRA_LDFLAGS} -X k8s.io/kops.Version=${VERSION}" k8s.io/kops/cmd/nodeup
crossbuild-nodeup-in-docker:
docker pull golang:${GOVERSION} # Keep golang image up to date
@ -95,9 +92,8 @@ crossbuild-nodeup-in-docker:
crossbuild:
mkdir -p .build/dist/
GOOS=darwin GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/darwin/amd64/kops -ldflags "${EXTRA_LDFLAGS} -X main.BuildVersion=${VERSION}" k8s.io/kops/cmd/kops
GOOS=linux GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/linux/amd64/kops -ldflags "${EXTRA_LDFLAGS} -X main.BuildVersion=${VERSION}" k8s.io/kops/cmd/kops
#GOOS=windows GOARCH=amd64 go build -o .build/dist/windows/amd64/kops -ldflags "-X main.BuildVersion=${VERSION}" -v k8s.io/kops/cmd/kops/...
GOOS=darwin GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/darwin/amd64/kops -ldflags "${EXTRA_LDFLAGS} -X k8s.io/kops.Version=${VERSION}" k8s.io/kops/cmd/kops
GOOS=linux GOARCH=amd64 go build -a ${EXTRA_BUILDFLAGS} -o .build/dist/linux/amd64/kops -ldflags "${EXTRA_LDFLAGS} -X k8s.io/kops.Version=${VERSION}" k8s.io/kops/cmd/kops
crossbuild-in-docker:
docker pull golang:${GOVERSION} # Keep golang image up to date
@ -109,12 +105,15 @@ kops-dist: crossbuild-in-docker
(sha1sum .build/dist/darwin/amd64/kops | cut -d' ' -f1) > .build/dist/darwin/amd64/kops.sha1
(sha1sum .build/dist/linux/amd64/kops | cut -d' ' -f1) > .build/dist/linux/amd64/kops.sha1
version-dist: nodeup-dist kops-dist
version-dist: nodeup-dist kops-dist protokube-export
rm -rf .build/upload
mkdir -p .build/upload/kops/${VERSION}/linux/amd64/
mkdir -p .build/upload/kops/${VERSION}/darwin/amd64/
mkdir -p .build/upload/kops/${VERSION}/images/
cp .build/dist/nodeup .build/upload/kops/${VERSION}/linux/amd64/nodeup
cp .build/dist/nodeup.sha1 .build/upload/kops/${VERSION}/linux/amd64/nodeup.sha1
cp .build/dist/images/protokube.tar.gz .build/upload/kops/${VERSION}/images/protokube.tar.gz
cp .build/dist/images/protokube.tar.gz.sha1 .build/upload/kops/${VERSION}/images/protokube.tar.gz.sha1
cp .build/dist/linux/amd64/kops .build/upload/kops/${VERSION}/linux/amd64/kops
cp .build/dist/linux/amd64/kops.sha1 .build/upload/kops/${VERSION}/linux/amd64/kops.sha1
cp .build/dist/darwin/amd64/kops .build/upload/kops/${VERSION}/darwin/amd64/kops
@ -154,7 +153,6 @@ push-aws-run: push
ssh -t ${TARGET} sudo SKIP_PACKAGE_UPDATE=1 /tmp/nodeup --conf=/var/cache/kubernetes-install/kube_env.yaml --v=8
protokube-gocode:
go install k8s.io/kops/protokube/cmd/protokube
@ -162,18 +160,26 @@ protokube-builder-image:
docker build -t protokube-builder images/protokube-builder
protokube-build-in-docker: protokube-builder-image
docker run -it -e VERSION=${VERSION} -v `pwd`:/src protokube-builder /onbuild.sh
docker run -t -e VERSION=${VERSION} -v `pwd`:/src protokube-builder /onbuild.sh
protokube-image: protokube-build-in-docker
docker build -t ${DOCKER_REGISTRY}/protokube:${PROTOKUBE_TAG} -f images/protokube/Dockerfile .
docker build -t protokube:${VERSION} -f images/protokube/Dockerfile .
protokube-export: protokube-image
mkdir -p .build/dist/images
docker save protokube:${VERSION} | gzip -c > .build/dist/images/protokube.tar.gz
(sha1sum .build/dist/images/protokube.tar.gz | cut -d' ' -f1) > .build/dist/images/protokube.tar.gz.sha1
# protokube-push is no longer used (we upload a docker image tar file to S3 instead),
# but we're keeping it around in case it is useful for development etc
protokube-push: protokube-image
docker push ${DOCKER_REGISTRY}/protokube:${PROTOKUBE_TAG}
docker tag protokube:${VERSION} ${DOCKER_REGISTRY}/protokube:${VERSION}
docker push ${DOCKER_REGISTRY}/protokube:${VERSION}
nodeup: nodeup-dist
nodeup-gocode: kops-gobindata
go install ${EXTRA_BUILDFLAGS} -ldflags "${EXTRA_LDFLAGS} -X main.BuildVersion=${VERSION}" k8s.io/kops/cmd/nodeup
go install ${EXTRA_BUILDFLAGS} -ldflags "${EXTRA_LDFLAGS} -X k8s.io/kops.Version=${VERSION}" k8s.io/kops/cmd/nodeup
nodeup-dist:
docker pull golang:${GOVERSION} # Keep golang image up to date
@ -189,7 +195,7 @@ dns-controller-builder-image:
docker build -t dns-controller-builder images/dns-controller-builder
dns-controller-build-in-docker: dns-controller-builder-image
docker run -it -e VERSION=${VERSION} -v `pwd`:/src dns-controller-builder /onbuild.sh
docker run -t -e VERSION=${VERSION} -v `pwd`:/src dns-controller-builder /onbuild.sh
dns-controller-image: dns-controller-build-in-docker
docker build -t ${DOCKER_REGISTRY}/dns-controller:${DNS_CONTROLLER_TAG} -f images/dns-controller/Dockerfile .
@ -258,7 +264,7 @@ ci: kops nodeup-gocode examples test govet verify-boilerplate verify-gofmt verif
channels: channels-gocode
channels-gocode:
go install ${EXTRA_BUILDFLAGS} -ldflags "-X main.BuildVersion=${VERSION} ${EXTRA_LDFLAGS}" k8s.io/kops/channels/cmd/channels
go install ${EXTRA_BUILDFLAGS} -ldflags "-X k8s.io/kops.Version=${VERSION} ${EXTRA_LDFLAGS}" k8s.io/kops/channels/cmd/channels
# --------------------------------------------------
# API / embedding examples
@ -270,8 +276,11 @@ examples:
# api machinery regenerate
apimachinery:
#go install ./cmd/libs/go2idl/conversion-gen
# conversion: go install ./cmd/libs/go2idl/conversion-gen
${GOPATH}/bin/conversion-gen --skip-unsafe=true --input-dirs k8s.io/kops/pkg/apis/kops/v1alpha1 --v=8 --output-file-base=zz_generated.conversion
# defaulters: go install ./cmd/libs/go2idl/defaulter-gen
${GOPATH}/bin/defaulter-gen --input-dirs k8s.io/kops/pkg/apis/kops/v1alpha1 --v=8 --output-file-base=zz_generated.defaults
${GOPATH}/bin/defaulter-gen --input-dirs k8s.io/kops/pkg/apis/kops/v1alpha2 --v=8 --output-file-base=zz_generated.defaults
#go install github.com/ugorji/go/codec/codecgen
# codecgen works only if invoked from directory where the file is located.
#cd pkg/apis/kops/v1alpha2/ && ~/k8s/bin/codecgen -d 1234 -o types.generated.go instancegroup.go cluster.go federation.go

View File

@ -12,7 +12,7 @@ The easiest way to get a production grade Kubernetes cluster up and running.
We like to think of it as `kubectl` for clusters.
`kops` lets you deploy grade Kubernetes clusters from the command line, with
`kops` lets you deploy production-grade Kubernetes clusters from the command line, with
options that support HA Masters. Kubernetes Operations supports deploying
Kubernetes on Amazon Web Services (AWS) and support for more platforms is planned.
@ -39,6 +39,8 @@ on AWS.
* Command line [autocomplete](/docs/cli/kops_completion.md)
* Community support
* [Upgrade from kube-up](/docs/upgrade_from_kubeup.md)
## Installing
`kubectl` is required, see [here](http://kubernetes.io/docs/user-guide/prereqs/).

@ -1 +1 @@
Subproject commit fa96ff3bb45eb37d516b2d9163c99fc76a5cc91a
Subproject commit 99939d360a807451ec26f32da23c226acb018feb

View File

@ -23,7 +23,7 @@ import (
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)
@ -79,7 +79,7 @@ func (c *RootCmd) AddCommand(cmd *cobra.Command) {
c.cobraCommand.AddCommand(cmd)
}
func (c *RootCmd) KubernetesClient() (*release_1_5.Clientset, error) {
func (c *RootCmd) KubernetesClient() (*k8s_clientset.Clientset, error) {
config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{})
@ -96,7 +96,7 @@ func (c *RootCmd) KubernetesClient() (*release_1_5.Clientset, error) {
}
}
k8sClient, err := release_1_5.NewForConfig(clientConfig)
k8sClient, err := k8s_clientset.NewForConfig(clientConfig)
if err != nil {
return nil, fmt.Errorf("cannot build kube client: %v", err)
}

View File

@ -17,13 +17,15 @@ limitations under the License.
package api
import (
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
v1 "k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
type Addons struct {
unversioned.TypeMeta `json:",inline"`
k8sapi.ObjectMeta `json:"metadata,omitempty"`
metav1.TypeMeta `json:",inline"`
// We use v1.ObjectMeta so we don't serialize everything, even though we are using the full api machinery here (yet!)
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
Spec AddonsSpec `json:"spec,omitempty"`
}

View File

@ -20,7 +20,7 @@ import (
"fmt"
"github.com/golang/glog"
"k8s.io/kops/channels/pkg/api"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/util/validation/field"
"net/url"
)
@ -57,7 +57,7 @@ func (a *Addon) buildChannel() *Channel {
}
return channel
}
func (a *Addon) GetRequiredUpdates(k8sClient *release_1_5.Clientset) (*AddonUpdate, error) {
func (a *Addon) GetRequiredUpdates(k8sClient *clientset.Clientset) (*AddonUpdate, error) {
newVersion := a.ChannelVersion()
channel := a.buildChannel()
@ -78,7 +78,7 @@ func (a *Addon) GetRequiredUpdates(k8sClient *release_1_5.Clientset) (*AddonUpda
}, nil
}
func (a *Addon) EnsureUpdated(k8sClient *release_1_5.Clientset) (*AddonUpdate, error) {
func (a *Addon) EnsureUpdated(k8sClient *clientset.Clientset) (*AddonUpdate, error) {
required, err := a.GetRequiredUpdates(k8sClient)
if err != nil {
return nil, err

View File

@ -82,7 +82,7 @@ func (a *Addons) GetCurrent() ([]*Addon, error) {
func (a *Addons) All() ([]*Addon, error) {
var addons []*Addon
for _, s := range a.APIObject.Spec.Addons {
name := a.APIObject.Name
name := a.APIObject.ObjectMeta.Name
if s.Name != nil {
name = *s.Name
}

View File

@ -23,7 +23,8 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"strings"
)
@ -115,8 +116,8 @@ func (c *ChannelVersion) Replaces(existing *ChannelVersion) bool {
return true
}
func (c *Channel) GetInstalledVersion(k8sClient *release_1_5.Clientset) (*ChannelVersion, error) {
ns, err := k8sClient.Namespaces().Get(c.Namespace)
func (c *Channel) GetInstalledVersion(k8sClient *clientset.Clientset) (*ChannelVersion, error) {
ns, err := k8sClient.Namespaces().Get(c.Namespace, metav1.GetOptions{})
if err != nil {
return nil, fmt.Errorf("error querying namespace %q: %v", c.Namespace, err)
}
@ -136,9 +137,9 @@ type annotationPatchMetadata struct {
Annotations map[string]string `json:"annotations,omitempty"`
}
func (c *Channel) SetInstalledVersion(k8sClient *release_1_5.Clientset, version *ChannelVersion) error {
func (c *Channel) SetInstalledVersion(k8sClient *clientset.Clientset, version *ChannelVersion) error {
// Primarily to check it exists
_, err := k8sClient.Namespaces().Get(c.Namespace)
_, err := k8sClient.Namespaces().Get(c.Namespace, metav1.GetOptions{})
if err != nil {
return fmt.Errorf("error querying namespace %q: %v", c.Namespace, err)
}

View File

@ -30,9 +30,9 @@ import (
"k8s.io/kops/util/pkg/vfs"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime/schema"
)
// TODO: Move to field on instancegroup?
@ -96,7 +96,7 @@ func RunCreate(f *util.Factory, out io.Writer, c *CreateOptions) error {
sections := bytes.Split(contents, []byte("\n---\n"))
for _, section := range sections {
defaults := &unversioned.GroupVersionKind{
defaults := &schema.GroupVersionKind{
Group: v1alpha1.SchemeGroupVersion.Group,
Version: v1alpha1.SchemeGroupVersion.Version,
}

View File

@ -78,6 +78,7 @@ func (o *CreateClusterOptions) InitDefaults() {
o.AssociatePublicIP = true
o.Channel = api.DefaultChannel
o.Topology = "public"
o.Bastion = false
}
func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
@ -143,7 +144,7 @@ func NewCmdCreateCluster(f *util.Factory, out io.Writer) *cobra.Command {
cmd.Flags().StringVarP(&options.Topology, "topology", "t", options.Topology, "Controls network topology for the cluster. public|private. Default is 'public'.")
// Bastion
cmd.Flags().BoolVar(&options.Bastion, "bastion", options.Bastion, "Specify --bastion=[true|false] to turn enable/disable bastion setup. Default to 'false' when topology is 'public' and defaults to 'true' if topology is 'private'.")
cmd.Flags().BoolVar(&options.Bastion, "bastion", options.Bastion, "Pass the --bastion flag to enable a bastion instance group. Only applies to private topology.")
return cmd
}
@ -332,7 +333,13 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
for _, masterName := range masterNames {
ig := masterInstanceGroups[masterName]
m := &api.EtcdMemberSpec{}
m.Name = ig.ObjectMeta.Name
name := ig.ObjectMeta.Name
// We expect the IG to have a `master-` prefix, but this is both superfluous
// and not how we named things previously
name = strings.TrimPrefix(name, "master-")
m.Name = name
m.InstanceGroup = fi.String(ig.ObjectMeta.Name)
etcd.Members = append(etcd.Members, m)
}
@ -473,16 +480,23 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
}
cluster.Spec.Subnets = append(cluster.Spec.Subnets, utilitySubnets...)
// Default to a DNS name for the bastion
cluster.Spec.Topology.Bastion = &api.BastionSpec{
BastionPublicName: "bastion-" + clusterName,
}
if c.Bastion {
bastionGroup := &api.InstanceGroup{}
bastionGroup.Spec.Role = api.InstanceGroupRoleBastion
bastionGroup.ObjectMeta.Name = "bastions"
instanceGroups = append(instanceGroups, bastionGroup)
// Logic to handle default bastion names
if c.DNSZone != "" {
cluster.Spec.Topology.Bastion = &api.BastionSpec{
BastionPublicName: "bastion-" + c.DNSZone,
}
} else {
// Use default zone and cluster name
cluster.Spec.Topology.Bastion = &api.BastionSpec{
BastionPublicName: "bastion-" + clusterName,
}
}
}
default:

View File

@ -24,14 +24,14 @@ import (
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/diff"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
"path"
"strings"
"testing"
"time"
)
var MagicTimestamp = unversioned.Time{Time: time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)}
var MagicTimestamp = v1.Time{Time: time.Date(2017, 1, 1, 0, 0, 0, 0, time.UTC)}
// TestCreateClusterMinimal runs kops create cluster minimal.example.com --zones us-test-1a
func TestCreateClusterMinimal(t *testing.T) {

View File

@ -18,15 +18,9 @@ package main // import "k8s.io/kops/cmd/kops"
import (
"fmt"
"k8s.io/kops/upup/pkg/fi/cloudup"
"os"
)
var (
// value overwritten during build. This can be used to resolve issues.
BuildVersion = cloudup.NodeUpVersion
)
func main() {
Execute()
}

View File

@ -25,7 +25,7 @@ import (
"k8s.io/kops/util/pkg/tables"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"os"
"strconv"
@ -97,9 +97,9 @@ func (c *RollingUpdateClusterCmd) Run(args []string) error {
}
var nodes []v1.Node
var k8sClient *release_1_5.Clientset
var k8sClient *k8s_clientset.Clientset
if !c.CloudOnly {
k8sClient, err = release_1_5.NewForConfig(config)
k8sClient, err = k8s_clientset.NewForConfig(config)
if err != nil {
return fmt.Errorf("cannot build kube client for %q: %v", contextName, err)
}

View File

@ -100,6 +100,7 @@ func NewCmdRoot(f *util.Factory, out io.Writer) *cobra.Command {
cmd.AddCommand(NewCmdEdit(f, out))
cmd.AddCommand(NewCmdUpdate(f, out))
cmd.AddCommand(NewCmdReplace(f, out))
cmd.AddCommand(NewCmdToolbox(f, out))
cmd.AddCommand(NewCmdValidate(f, out))
return cmd

View File

@ -18,14 +18,18 @@ package main
import (
"github.com/spf13/cobra"
"io"
"k8s.io/kops/cmd/kops/util"
)
// toolboxCmd represents the toolbox command
var toolboxCmd = &cobra.Command{
Use: "toolbox",
Short: "Misc infrequently used commands",
}
func NewCmdToolbox(f *util.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "toolbox",
Short: "Misc infrequently used commands",
}
func init() {
rootCommand.AddCommand(toolboxCmd)
cmd.AddCommand(NewCmdToolboxConvertImported(f, out))
cmd.AddCommand(NewCmdToolboxDump(f, out))
return cmd
}

View File

@ -19,46 +19,64 @@ package main
import (
"fmt"
"github.com/spf13/cobra"
"io"
"k8s.io/kops/cmd/kops/util"
api "k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/cloudup/awsup"
"k8s.io/kops/upup/pkg/kutil"
k8sapi "k8s.io/kubernetes/pkg/api"
)
type ConvertImportedCmd struct {
type ToolboxConvertImportedOptions struct {
NewClusterName string
// Channel is the location of the api.Channel to use for our defaults
Channel string
ClusterName string
}
var convertImported ConvertImportedCmd
func (o *ToolboxConvertImportedOptions) InitDefaults() {
o.Channel = api.DefaultChannel
}
func NewCmdToolboxConvertImported(f *util.Factory, out io.Writer) *cobra.Command {
options := &ToolboxConvertImportedOptions{}
func init() {
cmd := &cobra.Command{
Use: "convert-imported",
Short: "Convert an imported cluster into a kops cluster",
Run: func(cmd *cobra.Command, args []string) {
err := convertImported.Run()
if err := rootCommand.ProcessArgs(args); err != nil {
exitWithError(err)
}
options.ClusterName = rootCommand.ClusterName()
err := RunToolboxConvertImported(f, out, options)
if err != nil {
exitWithError(err)
}
},
}
toolboxCmd.AddCommand(cmd)
cmd.Flags().StringVar(&options.NewClusterName, "newname", options.NewClusterName, "new cluster name")
cmd.Flags().StringVar(&options.Channel, "channel", options.Channel, "Channel to use for upgrade")
cmd.Flags().StringVar(&convertImported.NewClusterName, "newname", "", "new cluster name")
cmd.Flags().StringVar(&convertImported.Channel, "channel", api.DefaultChannel, "Channel to use for upgrade")
return cmd
}
func (c *ConvertImportedCmd) Run() error {
cluster, err := rootCommand.Cluster()
func RunToolboxConvertImported(f *util.Factory, out io.Writer, options *ToolboxConvertImportedOptions) error {
clientset, err := f.Clientset()
if err != nil {
return err
}
clientset, err := rootCommand.Clientset()
if options.ClusterName == "" {
return fmt.Errorf("ClusterName is required")
}
cluster, err := clientset.Clusters().Get(options.ClusterName)
if err != nil {
return err
}
@ -76,7 +94,7 @@ func (c *ConvertImportedCmd) Run() error {
return fmt.Errorf("cluster %q does not appear to be a cluster imported using kops import", cluster.ObjectMeta.Name)
}
if c.NewClusterName == "" {
if options.NewClusterName == "" {
return fmt.Errorf("--newname is required for converting an imported cluster")
}
@ -110,13 +128,13 @@ func (c *ConvertImportedCmd) Run() error {
return fmt.Errorf("error initializing AWS client: %v", err)
}
channel, err := api.LoadChannel(c.Channel)
channel, err := api.LoadChannel(options.Channel)
if err != nil {
return err
}
d := &kutil.ConvertKubeupCluster{
NewClusterName: c.NewClusterName,
NewClusterName: options.NewClusterName,
OldClusterName: oldClusterName,
Cloud: cloud,
ClusterConfig: cluster,

146
cmd/kops/toolbox_dump.go Normal file
View File

@ -0,0 +1,146 @@
/*
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 main
import (
"encoding/json"
"fmt"
"github.com/golang/glog"
"github.com/spf13/cobra"
"io"
"k8s.io/kops/cmd/kops/util"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi/cloudup"
"k8s.io/kops/upup/pkg/kutil"
)
type ToolboxDumpOptions struct {
Output string
ClusterName string
}
func (o *ToolboxDumpOptions) InitDefaults() {
o.Output = OutputYaml
}
func NewCmdToolboxDump(f *util.Factory, out io.Writer) *cobra.Command {
options := &ToolboxDumpOptions{}
options.InitDefaults()
cmd := &cobra.Command{
Use: "dump",
Short: "Dump information about a cluster",
Run: func(cmd *cobra.Command, args []string) {
if err := rootCommand.ProcessArgs(args); err != nil {
exitWithError(err)
}
options.ClusterName = rootCommand.ClusterName()
err := RunToolboxDump(f, out, options)
if err != nil {
exitWithError(err)
}
},
}
// TODO: Push up to top-level command?
cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "output format. One of: yaml, json")
return cmd
}
func RunToolboxDump(f *util.Factory, out io.Writer, options *ToolboxDumpOptions) error {
clientset, err := f.Clientset()
if err != nil {
return err
}
if options.ClusterName == "" {
return fmt.Errorf("ClusterName is required")
}
cluster, err := clientset.Clusters().Get(options.ClusterName)
if err != nil {
return err
}
if cluster == nil {
return fmt.Errorf("cluster not found %q", options.ClusterName)
}
cloud, err := cloudup.BuildCloud(cluster)
if err != nil {
return err
}
d := &kutil.DeleteCluster{}
d.ClusterName = options.ClusterName
d.Cloud = cloud
resources, err := d.ListResources()
if err != nil {
return err
}
data := make(map[string]interface{})
dumpedResources := []interface{}{}
for k, r := range resources {
if r.Dumper == nil {
glog.V(8).Infof("skipping dump of %q (no Dumper)", k)
continue
}
o, err := r.Dumper(r)
if err != nil {
return fmt.Errorf("error dumping %q: %v", k, err)
}
if o != nil {
dumpedResources = append(dumpedResources, o)
}
}
data["resources"] = dumpedResources
switch options.Output {
case OutputYaml:
b, err := kops.ToRawYaml(data)
if err != nil {
return fmt.Errorf("error marshaling yaml: %v", err)
}
_, err = out.Write(b)
if err != nil {
return fmt.Errorf("error writing to stdout: %v", err)
}
return nil
case OutputJSON:
b, err := json.MarshalIndent(data, "", " ")
if err != nil {
return fmt.Errorf("error marshaling json: %v", err)
}
_, err = out.Write(b)
if err != nil {
return fmt.Errorf("error writing to stdout: %v", err)
}
return nil
default:
return fmt.Errorf("Unsupported output format: %q", options.Output)
}
}

View File

@ -30,7 +30,7 @@ import (
"k8s.io/kops/util/pkg/tables"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)
@ -99,7 +99,7 @@ func RunValidateCluster(f *util.Factory, cmd *cobra.Command, args []string, out
return fmt.Errorf("Cannot load kubecfg settings for %q: %v\n", contextName, err)
}
k8sClient, err := release_1_5.NewForConfig(config)
k8sClient, err := k8s_clientset.NewForConfig(config)
if err != nil {
return fmt.Errorf("Cannot build kube api client for %q: %v\n", contextName, err)
}

View File

@ -20,6 +20,7 @@ import (
"fmt"
"github.com/spf13/cobra"
"k8s.io/kops"
)
type VersionCmd struct {
@ -46,7 +47,7 @@ func init() {
}
func (c *VersionCmd) Run() error {
fmt.Printf("Version %s\n", BuildVersion)
fmt.Printf("Version %s\n", kops.Version)
return nil
}

View File

@ -0,0 +1,21 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
)
go_binary(
name = "dns-controller",
srcs = [
"main.go",
],
deps = [
"@com_github_golang_glog//:go_default_library",
"@com_github_spf13_pflag//:go_default_library",
"//dns-controller/pkg/dns:go_default_library",
"//dns-controller/pkg/watchers:go_default_library",
],
)

View File

@ -0,0 +1,24 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"dnscontext.go",
"dnscontroller.go",
"record.go",
"zonespec.go",
],
deps = [
"@com_github_golang_glog//:go_default_library",
"@io_k8s_kubernetes//federation/pkg/dnsprovider:go_default_library",
"@io_k8s_kubernetes//federation/pkg/dnsprovider/rrstype:go_default_library",
"//dns-controller/pkg/util:go_default_library",
],
)

View File

@ -32,6 +32,8 @@ import (
"k8s.io/kubernetes/federation/pkg/dnsprovider/rrstype"
)
const DefaultTTL = time.Minute
// DNSController applies the desired DNS state to the DNS backend
type DNSController struct {
zoneRules *ZoneRules
@ -252,10 +254,10 @@ func (c *DNSController) runOnce() error {
continue
}
ttl := 60
glog.Infof("Using default TTL of %d seconds", ttl)
ttl := DefaultTTL
glog.Infof("Using default TTL of %v", ttl)
err := op.updateRecords(k, newValues, int64(ttl))
err := op.updateRecords(k, newValues, int64(ttl.Seconds()))
if err != nil {
glog.Infof("error updating records for %s: %v", k, err)
errors = append(errors, err)
@ -362,10 +364,12 @@ func (o *dnsOp) findZone(fqdn string) dnsprovider.Zone {
func (o *dnsOp) deleteRecords(k recordKey) error {
glog.V(2).Infof("Deleting all records for %s", k)
zone := o.findZone(k.FQDN)
fqdn := EnsureDotSuffix(k.FQDN)
zone := o.findZone(fqdn)
if zone == nil {
// TODO: Post event into service / pod
return fmt.Errorf("no suitable zone found for %q", k.FQDN)
return fmt.Errorf("no suitable zone found for %q", fqdn)
}
rrsProvider, ok := zone.ResourceRecordSets()
@ -383,8 +387,8 @@ func (o *dnsOp) deleteRecords(k recordKey) error {
empty := true
for _, rr := range rrs {
rrName := EnsureDotSuffix(rr.Name())
if rrName != k.FQDN {
glog.V(8).Infof("Skipping delete of record %q (name != %s)", rrName, k.FQDN)
if rrName != fqdn {
glog.V(8).Infof("Skipping delete of record %q (name != %s)", rrName, fqdn)
continue
}
if string(rr.Type()) != string(k.RecordType) {
@ -411,10 +415,12 @@ func (o *dnsOp) deleteRecords(k recordKey) error {
func (o *dnsOp) updateRecords(k recordKey, newRecords []string, ttl int64) error {
glog.V(2).Infof("Updating records for %s: %v", k, newRecords)
zone := o.findZone(k.FQDN)
fqdn := EnsureDotSuffix(k.FQDN)
zone := o.findZone(fqdn)
if zone == nil {
// TODO: Post event into service / pod
return fmt.Errorf("no suitable zone found for %q", k.FQDN)
return fmt.Errorf("no suitable zone found for %q", fqdn)
}
rrsProvider, ok := zone.ResourceRecordSets()
@ -429,12 +435,13 @@ func (o *dnsOp) updateRecords(k recordKey, newRecords []string, ttl int64) error
var existing dnsprovider.ResourceRecordSet
for _, rr := range rrs {
if rr.Name() != k.FQDN {
glog.V(8).Infof("Skipping record %q (name != %s)", rr.Name(), k.FQDN)
rrName := EnsureDotSuffix(rr.Name())
if rrName != fqdn {
glog.V(8).Infof("Skipping record %q (name != %s)", rrName, fqdn)
continue
}
if string(rr.Type()) != string(k.RecordType) {
glog.V(8).Infof("Skipping record %q (type %s != %s)", rr.Name(), rr.Type(), k.RecordType)
glog.V(8).Infof("Skipping record %q (type %s != %s)", rrName, rr.Type(), k.RecordType)
continue
}
@ -451,11 +458,11 @@ func (o *dnsOp) updateRecords(k recordKey, newRecords []string, ttl int64) error
}
glog.V(2).Infof("Updating resource record %s %s", k, newRecords)
rr := rrsProvider.New(k.FQDN, newRecords, ttl, rrstype.RrsType(k.RecordType))
rr := rrsProvider.New(fqdn, newRecords, ttl, rrstype.RrsType(k.RecordType))
cs.Add(rr)
if err := cs.Apply(); err != nil {
return fmt.Errorf("error updating resource record %s %s: %v", k.FQDN, rr.Type(), err)
return fmt.Errorf("error updating resource record %s %s: %v", fqdn, rr.Type(), err)
}
return nil

View File

@ -0,0 +1,19 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"equality.go",
"stoppable.go",
],
deps = [
"@com_github_golang_glog//:go_default_library",
],
)

View File

@ -0,0 +1,23 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"annotations.go",
"ingresscontroller.go",
"nodecontroller.go",
"podcontroller.go",
"servicecontroller.go",
],
deps = [
"@com_github_golang_glog//:go_default_library",
"@io_k8s_kubernetes//pkg/api:go_default_library",
],
)

View File

@ -26,7 +26,7 @@ import (
"k8s.io/kops/dns-controller/pkg/util"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
client_extensions "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/extensions/v1beta1"
client_extensions "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1"
"k8s.io/kubernetes/pkg/watch"
)

View File

@ -25,7 +25,7 @@ import (
"k8s.io/kops/dns-controller/pkg/dns"
"k8s.io/kops/dns-controller/pkg/util"
"k8s.io/kubernetes/pkg/api/v1"
client "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/core/v1"
client "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
"k8s.io/kubernetes/pkg/watch"
)

View File

@ -25,7 +25,7 @@ import (
"k8s.io/kops/dns-controller/pkg/dns"
"k8s.io/kops/dns-controller/pkg/util"
"k8s.io/kubernetes/pkg/api/v1"
client "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/core/v1"
client "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
"k8s.io/kubernetes/pkg/watch"
"strings"
)

View File

@ -27,7 +27,7 @@ import (
"k8s.io/kops/dns-controller/pkg/dns"
"k8s.io/kops/dns-controller/pkg/util"
"k8s.io/kubernetes/pkg/api/v1"
client "k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/typed/core/v1"
client "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
"k8s.io/kubernetes/pkg/watch"
)

View File

@ -2,61 +2,84 @@
Bastion provide an external facing point of entry into a network containing private network instances. This host can provide a single point of fortification or audit and can be started and stopped to enable or disable inbound SSH communication from the Internet, some call bastion as the "jump server".
Note: Bastion will get setup for the cluster(by default) only when `--topology="private"`.
* [More information on bastion from aws](http://docs.aws.amazon.com/quickstart/latest/linux-bastion/architecture.html)
* [More information on bastion from gce](https://cloud.google.com/solutions/connecting-securely#bastion)
## AWS
### Specify instance type of bastion
### Enable/Disable bastion
Instance types in AWS comprise varying combinations of CPU, memory, storage, and networking capacity and give you the flexibility to choose the appropriate mix of resources for your applications.
To enable a bastion instance group, a user will need to set the `--bastion` flag on cluster create
- **Defaults** to `t2.medium`
- **Configure:** Bastion Instance type can be modified using `kops edit cluster`
```
topology:
kops create cluster --topology private --networking $provider --bastion $NAME
```
### Configure the bastion instance group
You can edit the bastion instance group to make changes. By default the name of the bastion instance group will be `bastions` and you can specify the name of the cluster with `--name` as in:
```
kops edit ig bastions --name $KOPS_NAME
```
You should now be able to edit and configure your bastion instance group.
```
apiVersion: kops/v1alpha2
kind: InstanceGroup
metadata:
creationTimestamp: "2017-01-05T13:37:07Z"
name: bastions
spec:
associatePublicIp: true
image: kope.io/k8s-1.4-debian-jessie-amd64-hvm-ebs-2016-10-21
machineType: t2.micro
maxSize: 1
minSize: 1
role: Bastion
subnets:
- utility-us-east-2a
```
**Note**: If you want to turn off the bastion server, you must set the instance group `maxSize` and `minSize` fields to `0`.
If you do not want the bastion instance group created at all, simply drop the `--bastion` flag off of your create command. The instance group will never be created.
### Using a public CNAME to access your bastion
By default the bastion instance group will create a public CNAME alias that will point to the bastion ELB.
The default bastion name is `bastion-$NAME` as in
```
bastion-example.kubernetes.com
```
Unless a user is using `--dns-zone` which will inherently use the `basion-$ZONE` syntax.
You can define a custom bastion CNAME by editing the main cluster config `kops edit cluster $NAME` and modifying the following block
```
spec:
topology:
bastion:
MachineType: c4.large
bastionPublicName: bastion-example.kubernetes.com
```
[More information](https://aws.amazon.com/ec2/instance-types/)
### Turn on/off bastion
### Changing your ELB idle timeout
The bastion is accessed via an AWS ELB. The ELB is required to gain secure access into the private network and connect the user to the ASG that the bastion lives in. Kops will by default set the bastion ELB idle timeout to 5 minutes. This is important for SSH connections to the bastion that you plan to keep open.
You can increase the ELB idle timeout by editing the main cluster config `kops edit cluster $NAME` and modifyng the following block
To turn on/off bastion host setup completely.
- **Defaults** to `false` if the topology selected is `public`
- **Defaults** to `true` if the topology selected is `private`
- **Configure:**
```
kops create cluster --bastion=[true|false]
```
OR using `kops edit cluster`
```
topology:
spec:
topology:
bastion:
Enable: true
idleTimeoutSeconds: 1200
```
### Reach bastion from outside of vpc using a name
- **Default:** CNAME for the bastion is only created when the user explicitly define it using `kops edit cluster`
- **Configure:** Bastion friendly CNAME can be configured using `kops edit cluster`
```
topology:
bastion:
PublicName: jumper
```
### High idle timeout for bastion ASG's ELB. (Configurable LoadBalancer Attributes)
By default, elastic load balancing sets the idle timeout to `60` seconds.
- **Default:** Bastion ELB in kops will have `120` seconds as their default timeout.
- **Configure:** This value can be configured using `kops edit cluster`
```
topology:
bastion:
IdleTimeOut: 75
```
[More information](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-idle-timeout.html)
Where the maximum value is 1200 seconds (20 minutes) allowed by AWS. [More information](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/config-idle-timeout.html)

View File

@ -32,6 +32,7 @@ It allows you to create, destroy, upgrade and maintain clusters.
* [kops export](kops_export.md) - export clusters/kubecfg
* [kops get](kops_get.md) - list or get objects
* [kops import](kops_import.md) - import clusters
* [kops replace](kops_replace.md) - Replace a resource by filename or stdin
* [kops rolling-update](kops_rolling-update.md) - rolling update clusters
* [kops secrets](kops_secrets.md) - Manage secrets & keys
* [kops toolbox](kops_toolbox.md) - Misc infrequently used commands

View File

@ -16,7 +16,7 @@ kops create cluster
```
--admin-access string Restrict access to admin endpoints (SSH, HTTPS) to this CIDR. If not set, access will not be restricted by IP.
--associate-public-ip Specify --associate-public-ip=[true|false] to enable/disable association of public IP for master ASG and nodes. Default is 'true'. (default true)
--bastion Specify --bastion=[true|false] to turn enable/disable bastion setup. Default to 'false' when topology is 'public' and defaults to 'true' if topology is 'private'.
--bastion Pass the --bastion flag to enable a bastion instance group. Only applies to private topology.
--channel string Channel for default versions and configuration to use (default "stable")
--cloud string Cloud provider to use - gce, aws
--dns-zone string DNS hosted zone to use (defaults to longest matching zone)
@ -26,7 +26,7 @@ kops create cluster
--master-zones string Zones in which to run masters (must be an odd number)
--model string Models to apply (separate multiple models with commas) (default "config,proto,cloudup")
--network-cidr string Set to override the default network CIDR
--networking string Networking mode to use. kubenet (default), classic, external, cni, kopeio-vxlan, weave. (default "kubenet")
--networking string Networking mode to use. kubenet (default), classic, external, cni, kopeio-vxlan, weave, calico. (default "kubenet")
--node-count int Set the number of nodes
--node-size string Set instance size for nodes
--out string Path to write any local output
@ -56,3 +56,4 @@ kops create cluster
### SEE ALSO
* [kops create](kops_create.md) - Create a resource by filename or stdin

View File

@ -11,6 +11,12 @@ Create an instancegroup configuration.
kops create instancegroup
```
### Options
```
--role string Type of instance group to create (Node,Master,Bastion) (default "Node")
```
### Options inherited from parent commands
```

View File

@ -10,7 +10,7 @@ list or get objects
### Options
```
-o, --output string output format. One of: table, yaml (default "table")
-o, --output string output format. One of: table, yaml, json (default "table")
```
### Options inherited from parent commands

View File

@ -26,7 +26,7 @@ kops get clusters
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default false)
--name string Name of cluster
-o, --output string output format. One of: table, yaml (default "table")
-o, --output string output format. One of: table, yaml, json (default "table")
--state string Location of state storage
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs

View File

@ -20,7 +20,7 @@ kops get federations
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default false)
--name string Name of cluster
-o, --output string output format. One of: table, yaml (default "table")
-o, --output string output format. One of: table, yaml, json (default "table")
--state string Location of state storage
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs

View File

@ -20,7 +20,7 @@ kops get instancegroups
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default false)
--name string Name of cluster
-o, --output string output format. One of: table, yaml (default "table")
-o, --output string output format. One of: table, yaml, json (default "table")
--state string Location of state storage
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs

View File

@ -26,7 +26,7 @@ kops get secrets
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default false)
--name string Name of cluster
-o, --output string output format. One of: table, yaml (default "table")
-o, --output string output format. One of: table, yaml, json (default "table")
--state string Location of state storage
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs

37
docs/cli/kops_replace.md Normal file
View File

@ -0,0 +1,37 @@
## kops replace
Replace a resource by filename or stdin
### Synopsis
Replace a resource by filename or stdin
```
kops replace -f FILENAME
```
### Options
```
-f, --filename stringSlice Filename to use to replace the resource
```
### Options inherited from parent commands
```
--alsologtostderr log to standard error as well as files
--config string config file (default is $HOME/.kops.yaml)
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default false)
--name string Name of cluster
--state string Location of state storage
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
### SEE ALSO
* [kops](kops.md) - kops is kubernetes ops

View File

@ -14,11 +14,12 @@ kops rolling-update cluster
### Options
```
--cloudonly Perform rolling update without confirming progress with k8s
--force Force rolling update, even if no changes
--master-interval duration Time to wait between restarting masters (default 5m0s)
--node-interval duration Time to wait between restarting nodes (default 2m0s)
--yes perform rolling update without confirmation
--bastion-interval duration Time to wait between restarting bastions (default 5m0s)
--cloudonly Perform rolling update without confirming progress with k8s
--force Force rolling update, even if no changes
--master-interval duration Time to wait between restarting masters (default 5m0s)
--node-interval duration Time to wait between restarting nodes (default 2m0s)
--yes perform rolling update without confirmation
```
### Options inherited from parent commands

View File

@ -25,4 +25,5 @@ Misc infrequently used commands
### SEE ALSO
* [kops](kops.md) - kops is kubernetes ops
* [kops toolbox convert-imported](kops_toolbox_convert-imported.md) - Convert an imported cluster into a kops cluster
* [kops toolbox dump](kops_toolbox_dump.md) - Dump information about a cluster

View File

@ -14,7 +14,7 @@ kops toolbox convert-imported
### Options
```
--channel string Channel to use for upgrade (default "stable")
--channel string Channel to use for upgrade
--newname string new cluster name
```

View File

@ -0,0 +1,37 @@
## kops toolbox dump
Dump information about a cluster
### Synopsis
Dump information about a cluster
```
kops toolbox dump
```
### Options
```
-o, --output string output format. One of: yaml, json (default "yaml")
```
### Options inherited from parent commands
```
--alsologtostderr log to standard error as well as files
--config string config file (default is $HOME/.kops.yaml)
--log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0)
--log_dir string If non-empty, write log files in this directory
--logtostderr log to standard error instead of files (default false)
--name string Name of cluster
--state string Location of state storage
--stderrthreshold severity logs at or above this threshold go to stderr (default 2)
-v, --v Level log level for V logs
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
```
### SEE ALSO
* [kops toolbox](kops_toolbox.md) - Misc infrequently used commands

View File

@ -4,7 +4,34 @@ This list is not complete, but aims to document any keys that are less than self
## spec
### adminAccess
### api
This object configures how we expose the API:
* `dns` will allow direct access to master instances, and configure DNS to point directly to the master nodes.
* `loadBalancer` will configure a load balancer (ELB) in front of the master nodes, and configure DNS to point to the ELB.
DNS example:
```yaml
spec:
api:
dns: {}
```
When configuring a LoadBalancer, you can also choose to have a public ELB or an internal (VPC only) ELB. The `type`
field should be `Public` or `Internal`.
```yaml
spec:
api:
loadBalancer:
type: Public
```
### sshAccess
This array configures the CIDRs that are able to ssh into nodes. On AWS this is manifested as inbound security group rules on the `nodes` and `master` security groups.
@ -12,7 +39,19 @@ Use this key to restrict cluster access to an office ip address range, for examp
```yaml
spec:
adminAccess:
sshAccess:
- 12.34.56.78/32
```
### apiAccess
This array configures the CIDRs that are able to access the kubernetes API. On AWS this is manifested as inbound security group rules on the ELB or master security groups.
Use this key to restrict cluster access to an office ip address range, for example.
```yaml
spec:
apiAccess:
- 12.34.56.78/32
```

View File

@ -198,7 +198,7 @@ and then push nodeup using:
export S3_BUCKET_NAME=<yourbucketname>
make upload S3_BUCKET=s3://${S3_BUCKET_NAME} VERSION=dev
export NODEUP_URL=https://${S3_BUCKET_NAME}.s3.amazonaws.com/kops/dev/linux/amd64/nodeup
export KOPS_BASE_URL=https://${S3_BUCKET_NAME}.s3.amazonaws.com/kops/dev/
kops create cluster <clustername> --zones us-east-1b
...

View File

@ -32,10 +32,11 @@ import (
"k8s.io/kops/upup/pkg/fi/fitasks"
"k8s.io/kops/upup/pkg/fi/k8sapi"
"k8s.io/kops/upup/pkg/kutil"
"k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_5"
federation_clientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
"k8s.io/kubernetes/pkg/api/errors"
k8sapiv1 "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"strings"
"text/template"
)
@ -111,7 +112,7 @@ func (o *ApplyFederationOperation) Run() error {
// TODO: sync clusters
var controllerKubernetesClients []release_1_5.Interface
var controllerKubernetesClients []k8s_clientset.Interface
for _, controller := range o.Federation.Spec.Controllers {
cluster, err := o.KopsClient.Clusters().Get(controller)
if err != nil {
@ -140,7 +141,7 @@ func (o *ApplyFederationOperation) Run() error {
if err != nil {
return err
}
federationControllerClient, err := federation_release_1_5.NewForConfig(federationRestConfig)
federationControllerClient, err := federation_clientset.NewForConfig(federationRestConfig)
if err != nil {
return err
}
@ -360,7 +361,7 @@ func (o *ApplyFederationOperation) executeTemplate(key string, templateDefinitio
func (o *ApplyFederationOperation) EnsureNamespace(c *fi.Context) error {
k8s := c.Target.(*kubernetes.KubernetesTarget).KubernetesClient
ns, err := k8s.Core().Namespaces().Get(o.namespace)
ns, err := k8s.Core().Namespaces().Get(o.namespace, meta_v1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
ns = nil
@ -380,7 +381,7 @@ func (o *ApplyFederationOperation) EnsureNamespace(c *fi.Context) error {
return nil
}
func (o *ApplyFederationOperation) ensureFederationNamespace(k8s federation_release_1_5.Interface, name string) (*k8sapiv1.Namespace, error) {
func (o *ApplyFederationOperation) ensureFederationNamespace(k8s federation_clientset.Interface, name string) (*k8sapiv1.Namespace, error) {
return mutateNamespace(k8s, name, func(n *k8sapiv1.Namespace) (*k8sapiv1.Namespace, error) {
if n == nil {
n = &k8sapiv1.Namespace{}

View File

@ -23,17 +23,18 @@ import (
"k8s.io/kops/pkg/apis/kops/registry"
"k8s.io/kops/upup/pkg/kutil"
"k8s.io/kubernetes/federation/apis/federation/v1beta1"
"k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_5"
"k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
)
type FederationCluster struct {
FederationNamespace string
ControllerKubernetesClients []release_1_5.Interface
FederationClient federation_release_1_5.Interface
ControllerKubernetesClients []k8s_clientset.Interface
FederationClient federation_clientset.Interface
ClusterSecretName string
@ -88,7 +89,7 @@ func (o *FederationCluster) Run(cluster *kopsapi.Cluster) error {
return nil
}
func (o *FederationCluster) ensureFederationSecret(k8s release_1_5.Interface, caCertData []byte, user kutil.KubectlUser) error {
func (o *FederationCluster) ensureFederationSecret(k8s k8s_clientset.Interface, caCertData []byte, user kutil.KubectlUser) error {
_, err := mutateSecret(k8s, o.FederationNamespace, o.ClusterSecretName, func(s *v1.Secret) (*v1.Secret, error) {
var kubeconfigData []byte
var err error
@ -149,7 +150,7 @@ func (o *FederationCluster) ensureFederationSecret(k8s release_1_5.Interface, ca
return err
}
func (o *FederationCluster) ensureFederationCluster(federationClient federation_release_1_5.Interface) error {
func (o *FederationCluster) ensureFederationCluster(federationClient federation_clientset.Interface) error {
_, err := mutateCluster(federationClient, o.ClusterName, func(c *v1beta1.Cluster) (*v1beta1.Cluster, error) {
if c == nil {
c = &v1beta1.Cluster{}
@ -176,9 +177,9 @@ func (o *FederationCluster) ensureFederationCluster(federationClient federation_
return err
}
func findCluster(k8s federation_release_1_5.Interface, name string) (*v1beta1.Cluster, error) {
func findCluster(k8s federation_clientset.Interface, name string) (*v1beta1.Cluster, error) {
glog.V(2).Infof("querying k8s for federation cluster %s", name)
c, err := k8s.Federation().Clusters().Get(name)
c, err := k8s.Federation().Clusters().Get(name, meta_v1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return nil, nil
@ -189,7 +190,7 @@ func findCluster(k8s federation_release_1_5.Interface, name string) (*v1beta1.Cl
return c, nil
}
func mutateCluster(k8s federation_release_1_5.Interface, name string, fn func(s *v1beta1.Cluster) (*v1beta1.Cluster, error)) (*v1beta1.Cluster, error) {
func mutateCluster(k8s federation_clientset.Interface, name string, fn func(s *v1beta1.Cluster) (*v1beta1.Cluster, error)) (*v1beta1.Cluster, error) {
existing, err := findCluster(k8s, name)
if err != nil {
return nil, err

View File

@ -26,7 +26,8 @@ import (
"k8s.io/kops/upup/pkg/kutil"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
)
const UserAdmin = "admin"
@ -339,9 +340,9 @@ func (o *FederationConfiguration) ensureSecretKubeconfig(c *fi.Context, caCert *
return err
}
func findSecret(k8s release_1_5.Interface, namespace, name string) (*v1.Secret, error) {
func findSecret(k8s k8s_clientset.Interface, namespace, name string) (*v1.Secret, error) {
glog.V(2).Infof("querying k8s for secret %s/%s", namespace, name)
s, err := k8s.Core().Secrets(namespace).Get(name)
s, err := k8s.Core().Secrets(namespace).Get(name, meta_v1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return nil, nil
@ -352,7 +353,7 @@ func findSecret(k8s release_1_5.Interface, namespace, name string) (*v1.Secret,
return s, nil
}
func mutateSecret(k8s release_1_5.Interface, namespace string, name string, fn func(s *v1.Secret) (*v1.Secret, error)) (*v1.Secret, error) {
func mutateSecret(k8s k8s_clientset.Interface, namespace string, name string, fn func(s *v1.Secret) (*v1.Secret, error)) (*v1.Secret, error) {
existing, err := findSecret(k8s, namespace, name)
if err != nil {
return nil, err

View File

@ -19,14 +19,15 @@ package federation
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/federation/client/clientset_generated/federation_release_1_5"
"k8s.io/kubernetes/federation/client/clientset_generated/federation_clientset"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
func findNamespace(k8s federation_release_1_5.Interface, name string) (*v1.Namespace, error) {
func findNamespace(k8s federation_clientset.Interface, name string) (*v1.Namespace, error) {
glog.V(2).Infof("querying k8s for federation Namespace %s", name)
c, err := k8s.Core().Namespaces().Get(name)
c, err := k8s.Core().Namespaces().Get(name, meta_v1.GetOptions{})
if err != nil {
if errors.IsNotFound(err) {
return nil, nil
@ -37,7 +38,7 @@ func findNamespace(k8s federation_release_1_5.Interface, name string) (*v1.Names
return c, nil
}
func mutateNamespace(k8s federation_release_1_5.Interface, name string, fn func(s *v1.Namespace) (*v1.Namespace, error)) (*v1.Namespace, error) {
func mutateNamespace(k8s federation_clientset.Interface, name string, fn func(s *v1.Namespace) (*v1.Namespace, error)) (*v1.Namespace, error) {
existing, err := findNamespace(k8s, name)
if err != nil {
return nil, err

View File

@ -22,13 +22,13 @@ import (
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/kutil"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
)
type KubernetesTarget struct {
//kubectlContext string
//keystore *k8sapi.KubernetesKeystore
KubernetesClient release_1_5.Interface
KubernetesClient k8s_clientset.Interface
cluster *kopsapi.Cluster
}
@ -50,7 +50,7 @@ func NewKubernetesTarget(clientset simple.Clientset, keystore fi.Keystore, clust
return nil, fmt.Errorf("error building configuration for cluster %q: %v", cluster.ObjectMeta.Name, err)
}
k8sClient, err := release_1_5.NewForConfig(clientConfig)
k8sClient, err := k8s_clientset.NewForConfig(clientConfig)
if err != nil {
return nil, fmt.Errorf("cannot build k8s client: %v", err)
}

View File

@ -34,6 +34,7 @@ k8s.io/kops/pkg/model/resources
k8s.io/kops/pkg/validation
k8s.io/kops/protokube/cmd/protokube
k8s.io/kops/protokube/pkg/protokube
k8s.io/kops/tests/integration/conversion
k8s.io/kops/upup/models
k8s.io/kops/upup/pkg/fi
k8s.io/kops/upup/pkg/fi/cloudup

View File

@ -17,6 +17,7 @@
from __future__ import print_function
import argparse
import difflib
import glob
import json
import mmap
@ -25,16 +26,28 @@ import re
import sys
parser = argparse.ArgumentParser()
parser.add_argument("filenames", help="list of files to check, all files if unspecified", nargs='*')
parser.add_argument(
"filenames",
help="list of files to check, all files if unspecified",
nargs='*')
rootdir = os.path.dirname(__file__) + "/../../"
rootdir = os.path.abspath(rootdir)
parser.add_argument("--rootdir", default=rootdir, help="root directory to examine")
parser.add_argument(
"--rootdir", default=rootdir, help="root directory to examine")
default_boilerplate_dir = os.path.join(rootdir, "hack/boilerplate")
parser.add_argument("--boilerplate-dir", default=default_boilerplate_dir)
parser.add_argument(
"--boilerplate-dir", default=default_boilerplate_dir)
parser.add_argument(
"-v", "--verbose",
help="give verbose output regarding why a file does not pass",
action="store_true")
args = parser.parse_args()
verbose_out = sys.stderr if args.verbose else open("/dev/null", "w")
def get_refs():
refs = {}
@ -52,7 +65,8 @@ def get_refs():
def file_passes(filename, refs, regexs):
try:
f = open(filename, 'r')
except:
except Exception as exc:
print("Unable to open %s: %s" % (filename, exc), file=verbose_out)
return False
data = f.read()
@ -79,6 +93,9 @@ def file_passes(filename, refs, regexs):
# if our test file is smaller than the reference it surely fails!
if len(ref) > len(data):
print('File %s smaller than reference (%d < %d)' %
(filename, len(data), len(ref)),
file=verbose_out)
return False
# trim our file to the same number of lines as the reference file
@ -87,9 +104,10 @@ def file_passes(filename, refs, regexs):
p = regexs["year"]
for d in data:
if p.search(d):
print('File %s is missing the year' % filename, file=verbose_out)
return False
# Replace all occurrences of the regex "2016|2015|2014" with "YEAR"
# Replace all occurrences of the regex "2017|2016|2015|2014" with "YEAR"
p = regexs["date"]
for i, d in enumerate(data):
(data[i], found) = p.subn('YEAR', d)
@ -98,6 +116,12 @@ def file_passes(filename, refs, regexs):
# if we don't match the reference at this point, fail
if ref != data:
print("Header in %s does not match reference, diff:" % filename, file=verbose_out)
if args.verbose:
print(file=verbose_out)
for line in difflib.unified_diff(ref, data, 'reference', filename, lineterm=''):
print(line, file=verbose_out)
print(file=verbose_out)
return False
return True
@ -105,7 +129,9 @@ def file_passes(filename, refs, regexs):
def file_extension(filename):
return os.path.splitext(filename)[1].split(".")[-1].lower()
skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh', "vendor", "test/e2e/generated/bindata.go"]
skipped_dirs = ['Godeps', 'third_party', '_gopath', '_output', '.git', 'cluster/env.sh',
"vendor", "test/e2e/generated/bindata.go", "hack/boilerplate/test",
"pkg/generated/bindata.go"]
def normalize_files(files):
newfiles = []
@ -149,8 +175,8 @@ def get_regexs():
regexs = {}
# Search for "YEAR" which exists in the boilerplate, but shouldn't in the real thing
regexs["year"] = re.compile( 'YEAR' )
# dates can be 2014, 2015 or 2016, company holder names can be anything
regexs["date"] = re.compile( '(2014|2015|2016)' )
# dates can be 2014, 2015, 2016, or 2017; company holder names can be anything
regexs["date"] = re.compile( '(2014|2015|2016|2017)' )
# strip // +build \n\n build constraints
regexs["go_build_constraints"] = re.compile(r"^(// \+build.*\n)+\n", re.MULTILINE)
# strip #!.* from shell scripts
@ -166,5 +192,7 @@ def main():
if not file_passes(filename, refs, regexs):
print(filename, file=sys.stdout)
return 0
if __name__ == "__main__":
sys.exit(main())

View File

@ -75,7 +75,7 @@ cd $KOPS_DIRECTORY/..
GIT_VER=git-$(git describe --always)
[ -z "$GIT_VER" ] && echo "we do not have GIT_VER something is very wrong" && exit 1;
NODEUP_URL="https://${NODEUP_BUCKET}.s3.amazonaws.com/kops/${GIT_VER}/linux/amd64/nodeup"
KOPS_BASE_URL="https://${NODEUP_BUCKET}.s3.amazonaws.com/kops/${GIT_VER}/"
echo ==========
echo "Starting build"
@ -94,7 +94,7 @@ kops delete cluster \
echo ==========
echo "Creating cluster ${CLUSTER_NAME}"
NODEUP_URL=${NODEUP_URL} kops create cluster \
KOPS_BASE_URL=${KOPS_BASE_URL} kops create cluster \
--name $CLUSTER_NAME \
--state $KOPS_STATE_STORE \
--node-count $NODE_COUNT \

View File

@ -25,7 +25,7 @@ do
for j in ${BAD_HEADERS}
do
:
HEADER=$(cat ${KUBE_ROOT}/hack/boilerplate/boilerplate.${i}.txt | sed 's/YEAR/2016/')
HEADER=$(cat ${KUBE_ROOT}/hack/boilerplate/boilerplate.${i}.txt | sed 's/YEAR/2017/')
value=$(<${j})
if [[ "$j" != *$i ]]
then

View File

@ -22,7 +22,7 @@ import (
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/util/pkg/vfs"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
"net/url"
)
@ -31,8 +31,8 @@ const DefaultChannel = "stable"
const AlphaChannel = "alpha"
type Channel struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
Spec ChannelSpec `json:"spec,omitempty"`
}

View File

@ -20,19 +20,19 @@ import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
)
type Cluster struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
Spec ClusterSpec `json:"spec,omitempty"`
}
type ClusterList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
v1.ListMeta `json:"metadata,omitempty"`
Items []Cluster `json:"items"`
}
@ -233,6 +233,33 @@ type ClusterSpec struct {
// Networking configuration
Networking *NetworkingSpec `json:"networking,omitempty"`
// API field controls how the API is exposed outside the cluster
API *AccessSpec `json:"api,omitempty"`
}
type AccessSpec struct {
DNS *DNSAccessSpec `json:"dns,omitempty"`
LoadBalancer *LoadBalancerAccessSpec `json:"loadBalancer,omitempty"`
}
func (s *AccessSpec) IsEmpty() bool {
return s.DNS == nil && s.LoadBalancer == nil
}
type DNSAccessSpec struct {
}
// LoadBalancerType string describes LoadBalancer types (public, internal)
type LoadBalancerType string
const (
LoadBalancerTypePublic LoadBalancerType = "Public"
LoadBalancerTypeInternal LoadBalancerType = "Internal"
)
type LoadBalancerAccessSpec struct {
Type LoadBalancerType `json:"type,omitempty"`
}
type KubeDNSConfig struct {
@ -371,23 +398,3 @@ func (c *Cluster) FillDefaults() error {
func (c *Cluster) SharedVPC() bool {
return c.Spec.NetworkID != ""
}
// --------------------------------------------------------------------------------------------
// Network Topology functions for template parsing
//
// Each of these functions can be used in the model templates
// The go template package currently only supports boolean
// operations, so the logic is mapped here as *Cluster functions.
//
// A function will need to be defined for all new topologies, if we plan to use them in the
// model templates.
// --------------------------------------------------------------------------------------------
func (c *Cluster) IsTopologyPrivate() bool {
return (c.Spec.Topology.Masters == TopologyPrivate && c.Spec.Topology.Nodes == TopologyPrivate)
}
func (c *Cluster) IsTopologyPublic() bool {
return (c.Spec.Topology.Masters == TopologyPublic && c.Spec.Topology.Nodes == TopologyPublic)
}
func (c *Cluster) IsTopologyPrivateMasters() bool {
return (c.Spec.Topology.Masters == TopologyPrivate && c.Spec.Topology.Nodes == TopologyPublic)
}

View File

@ -21,8 +21,8 @@ import (
"fmt"
"github.com/golang/glog"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
func decoder() runtime.Decoder {
@ -42,7 +42,7 @@ func encoder(version string) runtime.Encoder {
if !ok {
glog.Fatalf("no YAML serializer registered")
}
gv := unversioned.GroupVersion{Group: GroupName, Version: version}
gv := schema.GroupVersion{Group: GroupName, Version: version}
return k8sapi.Codecs.EncoderForVersion(yaml.Serializer, gv)
}
@ -65,6 +65,6 @@ func ToVersionedYamlWithVersion(obj runtime.Object, version string) ([]byte, err
return w.Bytes(), nil
}
func ParseVersionedYaml(data []byte) (runtime.Object, *unversioned.GroupVersionKind, error) {
func ParseVersionedYaml(data []byte) (runtime.Object, *schema.GroupVersionKind, error) {
return decoder().Decode(data, nil, nil)
}

View File

@ -382,6 +382,8 @@ type KubeAPIServerConfig struct {
AnonymousAuth *bool `json:"anonymousAuth,omitempty" flag:"anonymous-auth"`
KubeletPreferredAddressTypes []string `json:"kubeletPreferredAddressTypes,omitempty" flag:"kubelet-preferred-address-types"`
StorageBackend *string `json:"storageBackend,omitempty" flag:"storage-backend"`
}
type KubeControllerManagerConfig struct {

View File

@ -18,13 +18,13 @@ package kops
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
)
// Federation represents a federated set of kubernetes clusters
type Federation struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
Spec FederationSpec `json:"spec,omitempty"`
}
@ -37,8 +37,8 @@ type FederationSpec struct {
}
type FederationList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
v1.ListMeta `json:"metadata,omitempty"`
Items []Federation `json:"items"`
}

View File

@ -20,21 +20,21 @@ import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// InstanceGroup represents a group of instances (either nodes or masters) with the same configuration
type InstanceGroup struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
Spec InstanceGroupSpec `json:"spec,omitempty"`
}
type InstanceGroupList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
v1.TypeMeta `json:",inline"`
v1.ListMeta `json:"metadata,omitempty"`
Items []InstanceGroup `json:"items"`
}

View File

@ -17,8 +17,8 @@ limitations under the License.
package kops
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
var (
@ -30,15 +30,15 @@ var (
const GroupName = "kops"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) unversioned.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) unversioned.GroupResource {
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
@ -54,12 +54,12 @@ func addKnownTypes(scheme *runtime.Scheme) error {
return nil
}
func (obj *Cluster) GetObjectKind() unversioned.ObjectKind {
func (obj *Cluster) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}
func (obj *InstanceGroup) GetObjectKind() unversioned.ObjectKind {
func (obj *InstanceGroup) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}
func (obj *Federation) GetObjectKind() unversioned.ObjectKind {
func (obj *Federation) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}

View File

@ -17,13 +17,13 @@ limitations under the License.
package v1alpha1
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
type Cluster struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
// Spec defines the behavior of a Cluster.
Spec ClusterSpec `json:"spec,omitempty"`
@ -34,8 +34,8 @@ type Cluster struct {
}
type ClusterList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata,omitempty"`
Items []Cluster `json:"items"`
}
@ -229,6 +229,33 @@ type ClusterSpec struct {
// Networking configuration
Networking *NetworkingSpec `json:"networking,omitempty"`
// API field controls how the API is exposed outside the cluster
API *AccessSpec `json:"api,omitempty"`
}
type AccessSpec struct {
DNS *DNSAccessSpec `json:"dns,omitempty"`
LoadBalancer *LoadBalancerAccessSpec `json:"loadBalancer,omitempty"`
}
func (s *AccessSpec) IsEmpty() bool {
return s.DNS == nil && s.LoadBalancer == nil
}
type DNSAccessSpec struct {
}
// LoadBalancerType string describes LoadBalancer types (public, internal)
type LoadBalancerType string
const (
LoadBalancerTypePublic LoadBalancerType = "Public"
LoadBalancerTypeInternal LoadBalancerType = "Internal"
)
type LoadBalancerAccessSpec struct {
Type LoadBalancerType `json:"type,omitempty"`
}
type KubeDNSConfig struct {

View File

@ -379,6 +379,8 @@ type KubeAPIServerConfig struct {
AnonymousAuth *bool `json:"anonymousAuth,omitempty" flag:"anonymous-auth"`
KubeletPreferredAddressTypes []string `json:"kubeletPreferredAddressTypes,omitempty" flag:"kubelet-preferred-address-types"`
StorageBackend *string `json:"storageBackend,omitempty" flag:"storage-backend"`
}
type KubeControllerManagerConfig struct {

View File

@ -217,7 +217,6 @@ func Convert_kops_EtcdMemberSpec_To_v1alpha1_EtcdMemberSpec(in *kops.EtcdMemberS
}
zone = strings.TrimPrefix(zone, "master-")
out.Zone = &zone
out.Name = zone
} else {
out.Zone = nil
}

View File

@ -17,9 +17,48 @@ limitations under the License.
package v1alpha1
import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return nil
RegisterDefaults(scheme)
return scheme.AddDefaultingFuncs(
SetDefaults_ClusterSpec,
)
}
func SetDefaults_ClusterSpec(obj *ClusterSpec) {
if obj.Topology == nil {
obj.Topology = &TopologySpec{}
}
if obj.Topology.Masters == "" {
obj.Topology.Masters = TopologyPublic
}
if obj.Topology.Nodes == "" {
obj.Topology.Nodes = TopologyPublic
}
if obj.API == nil {
obj.API = &AccessSpec{}
}
if obj.API.IsEmpty() {
switch obj.Topology.Masters {
case TopologyPublic:
obj.API.DNS = &DNSAccessSpec{}
case TopologyPrivate:
obj.API.LoadBalancer = &LoadBalancerAccessSpec{}
default:
glog.Infof("unknown master topology type: %q", obj.Topology.Masters)
}
}
if obj.API.LoadBalancer != nil && obj.API.LoadBalancer.Type == "" {
obj.API.LoadBalancer.Type = LoadBalancerTypePublic
}
}

View File

@ -15,4 +15,5 @@ limitations under the License.
*/
// +k8s:conversion-gen=k8s.io/kops/pkg/apis/kops
// +k8s:defaulter-gen=TypeMeta
package v1alpha1

View File

@ -17,13 +17,13 @@ limitations under the License.
package v1alpha1
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
type Federation struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
Spec FederationSpec `json:"spec"`
}
@ -36,8 +36,8 @@ type FederationSpec struct {
}
type FederationList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata,omitempty"`
Items []Federation `json:"items"`
}

View File

@ -17,21 +17,21 @@ limitations under the License.
package v1alpha1
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
// InstanceGroup represents a group of instances (either nodes or masters) with the same configuration
type InstanceGroup struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
Spec InstanceGroupSpec `json:"spec,omitempty"`
}
type InstanceGroupList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata,omitempty"`
Items []InstanceGroup `json:"items"`
}

View File

@ -17,15 +17,13 @@ limitations under the License.
package v1alpha1
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
var (
// TODO: Defaulting functions
//SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addConversionFuncs)
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
//SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
@ -34,15 +32,15 @@ var (
const GroupName = "kops"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha1"}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) unversioned.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) unversioned.GroupResource {
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
@ -61,13 +59,13 @@ func addKnownTypes(scheme *runtime.Scheme) error {
return nil
}
func (obj *Cluster) GetObjectKind() unversioned.ObjectKind {
func (obj *Cluster) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}
func (obj *InstanceGroup) GetObjectKind() unversioned.ObjectKind {
func (obj *InstanceGroup) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}
func (obj *Federation) GetObjectKind() unversioned.ObjectKind {
func (obj *Federation) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}

View File

@ -1,7 +1,7 @@
// +build !ignore_autogenerated
/*
Copyright 2016 The Kubernetes Authors.
Copyright 2017 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.
@ -34,6 +34,8 @@ func init() {
// Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs(
Convert_v1alpha1_AccessSpec_To_kops_AccessSpec,
Convert_kops_AccessSpec_To_v1alpha1_AccessSpec,
Convert_v1alpha1_CNINetworkingSpec_To_kops_CNINetworkingSpec,
Convert_kops_CNINetworkingSpec_To_v1alpha1_CNINetworkingSpec,
Convert_v1alpha1_CalicoNetworkingSpec_To_kops_CalicoNetworkingSpec,
@ -46,6 +48,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_kops_ClusterList_To_v1alpha1_ClusterList,
Convert_v1alpha1_ClusterSpec_To_kops_ClusterSpec,
Convert_kops_ClusterSpec_To_v1alpha1_ClusterSpec,
Convert_v1alpha1_DNSAccessSpec_To_kops_DNSAccessSpec,
Convert_kops_DNSAccessSpec_To_v1alpha1_DNSAccessSpec,
Convert_v1alpha1_DockerConfig_To_kops_DockerConfig,
Convert_kops_DockerConfig_To_v1alpha1_DockerConfig,
Convert_v1alpha1_EtcdClusterSpec_To_kops_EtcdClusterSpec,
@ -84,6 +88,8 @@ func RegisterConversions(scheme *runtime.Scheme) error {
Convert_kops_KubenetNetworkingSpec_To_v1alpha1_KubenetNetworkingSpec,
Convert_v1alpha1_LeaderElectionConfiguration_To_kops_LeaderElectionConfiguration,
Convert_kops_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfiguration,
Convert_v1alpha1_LoadBalancerAccessSpec_To_kops_LoadBalancerAccessSpec,
Convert_kops_LoadBalancerAccessSpec_To_v1alpha1_LoadBalancerAccessSpec,
Convert_v1alpha1_NetworkingSpec_To_kops_NetworkingSpec,
Convert_kops_NetworkingSpec_To_v1alpha1_NetworkingSpec,
Convert_v1alpha1_WeaveNetworkingSpec_To_kops_WeaveNetworkingSpec,
@ -91,6 +97,58 @@ func RegisterConversions(scheme *runtime.Scheme) error {
)
}
func autoConvert_v1alpha1_AccessSpec_To_kops_AccessSpec(in *AccessSpec, out *kops.AccessSpec, s conversion.Scope) error {
if in.DNS != nil {
in, out := &in.DNS, &out.DNS
*out = new(kops.DNSAccessSpec)
if err := Convert_v1alpha1_DNSAccessSpec_To_kops_DNSAccessSpec(*in, *out, s); err != nil {
return err
}
} else {
out.DNS = nil
}
if in.LoadBalancer != nil {
in, out := &in.LoadBalancer, &out.LoadBalancer
*out = new(kops.LoadBalancerAccessSpec)
if err := Convert_v1alpha1_LoadBalancerAccessSpec_To_kops_LoadBalancerAccessSpec(*in, *out, s); err != nil {
return err
}
} else {
out.LoadBalancer = nil
}
return nil
}
func Convert_v1alpha1_AccessSpec_To_kops_AccessSpec(in *AccessSpec, out *kops.AccessSpec, s conversion.Scope) error {
return autoConvert_v1alpha1_AccessSpec_To_kops_AccessSpec(in, out, s)
}
func autoConvert_kops_AccessSpec_To_v1alpha1_AccessSpec(in *kops.AccessSpec, out *AccessSpec, s conversion.Scope) error {
if in.DNS != nil {
in, out := &in.DNS, &out.DNS
*out = new(DNSAccessSpec)
if err := Convert_kops_DNSAccessSpec_To_v1alpha1_DNSAccessSpec(*in, *out, s); err != nil {
return err
}
} else {
out.DNS = nil
}
if in.LoadBalancer != nil {
in, out := &in.LoadBalancer, &out.LoadBalancer
*out = new(LoadBalancerAccessSpec)
if err := Convert_kops_LoadBalancerAccessSpec_To_v1alpha1_LoadBalancerAccessSpec(*in, *out, s); err != nil {
return err
}
} else {
out.LoadBalancer = nil
}
return nil
}
func Convert_kops_AccessSpec_To_v1alpha1_AccessSpec(in *kops.AccessSpec, out *AccessSpec, s conversion.Scope) error {
return autoConvert_kops_AccessSpec_To_v1alpha1_AccessSpec(in, out, s)
}
func autoConvert_v1alpha1_CNINetworkingSpec_To_kops_CNINetworkingSpec(in *CNINetworkingSpec, out *kops.CNINetworkingSpec, s conversion.Scope) error {
return nil
}
@ -140,7 +198,10 @@ func Convert_kops_ClassicNetworkingSpec_To_v1alpha1_ClassicNetworkingSpec(in *ko
}
func autoConvert_v1alpha1_Cluster_To_kops_Cluster(in *Cluster, out *kops.Cluster, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_v1alpha1_ClusterSpec_To_kops_ClusterSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -152,7 +213,10 @@ func Convert_v1alpha1_Cluster_To_kops_Cluster(in *Cluster, out *kops.Cluster, s
}
func autoConvert_kops_Cluster_To_v1alpha1_Cluster(in *kops.Cluster, out *Cluster, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -327,6 +391,15 @@ func autoConvert_v1alpha1_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *
} else {
out.Networking = nil
}
if in.API != nil {
in, out := &in.API, &out.API
*out = new(kops.AccessSpec)
if err := Convert_v1alpha1_AccessSpec_To_kops_AccessSpec(*in, *out, s); err != nil {
return err
}
} else {
out.API = nil
}
return nil
}
@ -454,9 +527,34 @@ func autoConvert_kops_ClusterSpec_To_v1alpha1_ClusterSpec(in *kops.ClusterSpec,
} else {
out.Networking = nil
}
if in.API != nil {
in, out := &in.API, &out.API
*out = new(AccessSpec)
if err := Convert_kops_AccessSpec_To_v1alpha1_AccessSpec(*in, *out, s); err != nil {
return err
}
} else {
out.API = nil
}
return nil
}
func autoConvert_v1alpha1_DNSAccessSpec_To_kops_DNSAccessSpec(in *DNSAccessSpec, out *kops.DNSAccessSpec, s conversion.Scope) error {
return nil
}
func Convert_v1alpha1_DNSAccessSpec_To_kops_DNSAccessSpec(in *DNSAccessSpec, out *kops.DNSAccessSpec, s conversion.Scope) error {
return autoConvert_v1alpha1_DNSAccessSpec_To_kops_DNSAccessSpec(in, out, s)
}
func autoConvert_kops_DNSAccessSpec_To_v1alpha1_DNSAccessSpec(in *kops.DNSAccessSpec, out *DNSAccessSpec, s conversion.Scope) error {
return nil
}
func Convert_kops_DNSAccessSpec_To_v1alpha1_DNSAccessSpec(in *kops.DNSAccessSpec, out *DNSAccessSpec, s conversion.Scope) error {
return autoConvert_kops_DNSAccessSpec_To_v1alpha1_DNSAccessSpec(in, out, s)
}
func autoConvert_v1alpha1_DockerConfig_To_kops_DockerConfig(in *DockerConfig, out *kops.DockerConfig, s conversion.Scope) error {
out.Bridge = in.Bridge
out.LogLevel = in.LogLevel
@ -568,7 +666,10 @@ func Convert_kops_ExternalNetworkingSpec_To_v1alpha1_ExternalNetworkingSpec(in *
}
func autoConvert_v1alpha1_Federation_To_kops_Federation(in *Federation, out *kops.Federation, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_v1alpha1_FederationSpec_To_kops_FederationSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -580,7 +681,10 @@ func Convert_v1alpha1_Federation_To_kops_Federation(in *Federation, out *kops.Fe
}
func autoConvert_kops_Federation_To_v1alpha1_Federation(in *kops.Federation, out *Federation, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_kops_FederationSpec_To_v1alpha1_FederationSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -654,7 +758,10 @@ func Convert_kops_FederationSpec_To_v1alpha1_FederationSpec(in *kops.FederationS
}
func autoConvert_v1alpha1_InstanceGroup_To_kops_InstanceGroup(in *InstanceGroup, out *kops.InstanceGroup, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_v1alpha1_InstanceGroupSpec_To_kops_InstanceGroupSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -666,7 +773,10 @@ func Convert_v1alpha1_InstanceGroup_To_kops_InstanceGroup(in *InstanceGroup, out
}
func autoConvert_kops_InstanceGroup_To_v1alpha1_InstanceGroup(in *kops.InstanceGroup, out *InstanceGroup, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
// TODO: Inefficient conversion - can we improve it?
if err := s.Convert(&in.ObjectMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := Convert_kops_InstanceGroupSpec_To_v1alpha1_InstanceGroupSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
@ -787,6 +897,7 @@ func autoConvert_v1alpha1_KubeAPIServerConfig_To_kops_KubeAPIServerConfig(in *Ku
out.RuntimeConfig = in.RuntimeConfig
out.AnonymousAuth = in.AnonymousAuth
out.KubeletPreferredAddressTypes = in.KubeletPreferredAddressTypes
out.StorageBackend = in.StorageBackend
return nil
}
@ -816,6 +927,7 @@ func autoConvert_kops_KubeAPIServerConfig_To_v1alpha1_KubeAPIServerConfig(in *ko
out.RuntimeConfig = in.RuntimeConfig
out.AnonymousAuth = in.AnonymousAuth
out.KubeletPreferredAddressTypes = in.KubeletPreferredAddressTypes
out.StorageBackend = in.StorageBackend
return nil
}
@ -1063,6 +1175,24 @@ func Convert_kops_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfigur
return autoConvert_kops_LeaderElectionConfiguration_To_v1alpha1_LeaderElectionConfiguration(in, out, s)
}
func autoConvert_v1alpha1_LoadBalancerAccessSpec_To_kops_LoadBalancerAccessSpec(in *LoadBalancerAccessSpec, out *kops.LoadBalancerAccessSpec, s conversion.Scope) error {
out.Type = kops.LoadBalancerType(in.Type)
return nil
}
func Convert_v1alpha1_LoadBalancerAccessSpec_To_kops_LoadBalancerAccessSpec(in *LoadBalancerAccessSpec, out *kops.LoadBalancerAccessSpec, s conversion.Scope) error {
return autoConvert_v1alpha1_LoadBalancerAccessSpec_To_kops_LoadBalancerAccessSpec(in, out, s)
}
func autoConvert_kops_LoadBalancerAccessSpec_To_v1alpha1_LoadBalancerAccessSpec(in *kops.LoadBalancerAccessSpec, out *LoadBalancerAccessSpec, s conversion.Scope) error {
out.Type = LoadBalancerType(in.Type)
return nil
}
func Convert_kops_LoadBalancerAccessSpec_To_v1alpha1_LoadBalancerAccessSpec(in *kops.LoadBalancerAccessSpec, out *LoadBalancerAccessSpec, s conversion.Scope) error {
return autoConvert_kops_LoadBalancerAccessSpec_To_v1alpha1_LoadBalancerAccessSpec(in, out, s)
}
func autoConvert_v1alpha1_NetworkingSpec_To_kops_NetworkingSpec(in *NetworkingSpec, out *kops.NetworkingSpec, s conversion.Scope) error {
if in.Classic != nil {
in, out := &in.Classic, &out.Classic

View File

@ -0,0 +1,45 @@
// +build !ignore_autogenerated
/*
Copyright 2017 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.
*/
// This file was autogenerated by defaulter-gen. Do not edit it manually!
package v1alpha1
import (
runtime "k8s.io/kubernetes/pkg/runtime"
)
// 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(&Cluster{}, func(obj interface{}) { SetObjectDefaults_Cluster(obj.(*Cluster)) })
scheme.AddTypeDefaultingFunc(&ClusterList{}, func(obj interface{}) { SetObjectDefaults_ClusterList(obj.(*ClusterList)) })
return nil
}
func SetObjectDefaults_Cluster(in *Cluster) {
SetDefaults_ClusterSpec(&in.Spec)
}
func SetObjectDefaults_ClusterList(in *ClusterList) {
for i := range in.Items {
a := &in.Items[i]
SetObjectDefaults_Cluster(a)
}
}

View File

@ -17,20 +17,20 @@ limitations under the License.
package v1alpha2
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
type Cluster struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
Spec ClusterSpec `json:"spec,omitempty"`
}
type ClusterList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata,omitempty"`
Items []Cluster `json:"items"`
}
@ -149,6 +149,33 @@ type ClusterSpec struct {
// Networking configuration
Networking *NetworkingSpec `json:"networking,omitempty"`
// API field controls how the API is exposed outside the cluster
API *AccessSpec `json:"api,omitempty"`
}
type AccessSpec struct {
DNS *DNSAccessSpec `json:"dns,omitempty"`
LoadBalancer *LoadBalancerAccessSpec `json:"loadBalancer,omitempty"`
}
func (s *AccessSpec) IsEmpty() bool {
return s.DNS == nil && s.LoadBalancer == nil
}
type DNSAccessSpec struct {
}
// LoadBalancerType string describes LoadBalancer types (public, internal)
type LoadBalancerType string
const (
LoadBalancerTypePublic LoadBalancerType = "Public"
LoadBalancerTypeInternal LoadBalancerType = "Internal"
)
type LoadBalancerAccessSpec struct {
Type LoadBalancerType `json:"type,omitempty"`
}
type KubeDNSConfig struct {

View File

@ -137,6 +137,8 @@ type KubeAPIServerConfig struct {
AnonymousAuth *bool `json:"anonymousAuth,omitempty" flag:"anonymous-auth"`
KubeletPreferredAddressTypes []string `json:"kubeletPreferredAddressTypes,omitempty" flag:"kubelet-preferred-address-types"`
StorageBackend *string `json:"storageBackend,omitempty" flag:"storage-backend"`
}
type KubeControllerManagerConfig struct {

View File

@ -0,0 +1,64 @@
/*
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 v1alpha2
import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
RegisterDefaults(scheme)
return scheme.AddDefaultingFuncs(
SetDefaults_ClusterSpec,
)
}
func SetDefaults_ClusterSpec(obj *ClusterSpec) {
if obj.Topology == nil {
obj.Topology = &TopologySpec{}
}
if obj.Topology.Masters == "" {
obj.Topology.Masters = TopologyPublic
}
if obj.Topology.Nodes == "" {
obj.Topology.Nodes = TopologyPublic
}
if obj.API == nil {
obj.API = &AccessSpec{}
}
if obj.API.IsEmpty() {
switch obj.Topology.Masters {
case TopologyPublic:
obj.API.DNS = &DNSAccessSpec{}
case TopologyPrivate:
obj.API.LoadBalancer = &LoadBalancerAccessSpec{}
default:
glog.Infof("unknown master topology type: %q", obj.Topology.Masters)
}
}
if obj.API.LoadBalancer != nil && obj.API.LoadBalancer.Type == "" {
obj.API.LoadBalancer.Type = LoadBalancerTypePublic
}
}

View File

@ -14,4 +14,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
// +k8s:conversion-gen=k8s.io/kops/pkg/apis/kops
// +k8s:defaulter-gen=TypeMeta
package v1alpha2 // import "k8s.io/kops/pkg/apis/kops/v1alpha2"

View File

@ -17,14 +17,14 @@ limitations under the License.
package v1alpha2
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
// Federation represents a federated set of kubernetes clusters
type Federation struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
Spec FederationSpec `json:"spec,omitempty"`
}
@ -37,8 +37,8 @@ type FederationSpec struct {
}
type FederationList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata,omitempty"`
Items []Federation `json:"items"`
}

View File

@ -17,21 +17,21 @@ limitations under the License.
package v1alpha2
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
// InstanceGroup represents a group of instances (either nodes or masters) with the same configuration
type InstanceGroup struct {
unversioned.TypeMeta `json:",inline"`
ObjectMeta api.ObjectMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
ObjectMeta v1.ObjectMeta `json:"metadata,omitempty"`
Spec InstanceGroupSpec `json:"spec,omitempty"`
}
type InstanceGroupList struct {
unversioned.TypeMeta `json:",inline"`
unversioned.ListMeta `json:"metadata,omitempty"`
meta_v1.TypeMeta `json:",inline"`
meta_v1.ListMeta `json:"metadata,omitempty"`
Items []InstanceGroup `json:"items"`
}

View File

@ -17,15 +17,14 @@ limitations under the License.
package v1alpha2
import (
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
var (
// TODO: Defaulting functions
//SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addConversionFuncs)
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs, addConversionFuncs)
//SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
AddToScheme = SchemeBuilder.AddToScheme
)
@ -34,15 +33,15 @@ var (
const GroupName = "kops"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = unversioned.GroupVersion{Group: GroupName, Version: "v1alpha2"}
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha2"}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) unversioned.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) unversioned.GroupResource {
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
@ -61,13 +60,13 @@ func addKnownTypes(scheme *runtime.Scheme) error {
return nil
}
func (obj *Cluster) GetObjectKind() unversioned.ObjectKind {
func (obj *Cluster) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}
func (obj *InstanceGroup) GetObjectKind() unversioned.ObjectKind {
func (obj *InstanceGroup) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}
func (obj *Federation) GetObjectKind() unversioned.ObjectKind {
func (obj *Federation) GetObjectKind() schema.ObjectKind {
return &obj.TypeMeta
}

View File

@ -0,0 +1,45 @@
// +build !ignore_autogenerated
/*
Copyright 2017 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.
*/
// This file was autogenerated by defaulter-gen. Do not edit it manually!
package v1alpha2
import (
runtime "k8s.io/kubernetes/pkg/runtime"
)
// 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(&Cluster{}, func(obj interface{}) { SetObjectDefaults_Cluster(obj.(*Cluster)) })
scheme.AddTypeDefaultingFunc(&ClusterList{}, func(obj interface{}) { SetObjectDefaults_ClusterList(obj.(*ClusterList)) })
return nil
}
func SetObjectDefaults_Cluster(in *Cluster) {
SetDefaults_ClusterSpec(&in.Spec)
}
func SetObjectDefaults_ClusterList(in *ClusterList) {
for i := range in.Items {
a := &in.Items[i]
SetObjectDefaults_Cluster(a)
}
}

View File

@ -25,7 +25,7 @@ import (
"k8s.io/kops/pkg/client/simple"
"k8s.io/kops/util/pkg/vfs"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
"os"
"strings"
"time"
@ -91,7 +91,7 @@ func (r *ClusterVFS) Create(c *api.Cluster) (*api.Cluster, error) {
}
if c.ObjectMeta.CreationTimestamp.IsZero() {
c.ObjectMeta.CreationTimestamp = unversioned.NewTime(time.Now().UTC())
c.ObjectMeta.CreationTimestamp = v1.NewTime(time.Now().UTC())
}
clusterName := c.ObjectMeta.Name

View File

@ -24,8 +24,9 @@ import (
"k8s.io/kops/pkg/apis/kops/v1alpha2"
"k8s.io/kops/util/pkg/vfs"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"os"
"reflect"
"sort"
@ -39,7 +40,7 @@ type commonVFS struct {
basePath vfs.Path
decoder runtime.Decoder
encoder runtime.Encoder
defaultReadVersion *unversioned.GroupVersionKind
defaultReadVersion *schema.GroupVersionKind
}
func (c *commonVFS) init(kind string, basePath vfs.Path, storeVersion runtime.GroupVersioner) {
@ -81,7 +82,7 @@ func (c *commonVFS) create(i runtime.Object) error {
}
if objectMeta.CreationTimestamp.IsZero() {
objectMeta.CreationTimestamp = unversioned.NewTime(time.Now().UTC())
objectMeta.CreationTimestamp = v1.NewTime(time.Now().UTC())
}
err = c.writeConfig(c.basePath.Join(objectMeta.Name), i, vfs.WriteOptionCreate)
@ -172,7 +173,7 @@ func (c *commonVFS) update(i runtime.Object) error {
}
if objectMeta.CreationTimestamp.IsZero() {
objectMeta.CreationTimestamp = unversioned.NewTime(time.Now().UTC())
objectMeta.CreationTimestamp = v1.NewTime(time.Now().UTC())
}
err = c.writeConfig(c.basePath.Join(objectMeta.Name), i, vfs.WriteOptionOnlyIfExists)

View File

@ -31,12 +31,17 @@ type APILoadBalancerBuilder struct {
var _ fi.ModelBuilder = &APILoadBalancerBuilder{}
func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
// Configuration where an ELB fronts the master (apiservers in particular)
// Configuration where an ELB fronts the API
if !b.UseLoadBalancerForAPI() {
return nil
}
lbSpec := b.Cluster.Spec.API.LoadBalancer
if lbSpec == nil {
// Skipping API ELB creation; not requested in Spec
return nil
}
var elb *awstasks.LoadBalancer
{
elbID, err := b.GetELBName32("api")
@ -50,11 +55,12 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
switch subnet.Type {
case kops.SubnetTypePublic:
if !b.Cluster.IsTopologyPublic() {
if b.Cluster.Spec.Topology.Masters != kops.TopologyPublic {
continue
}
case kops.SubnetTypeUtility:
if !b.Cluster.IsTopologyPrivate() {
if b.Cluster.Spec.Topology.Masters != kops.TopologyPrivate {
continue
}
@ -89,6 +95,15 @@ func (b *APILoadBalancerBuilder) Build(c *fi.ModelBuilderContext) error {
},
}
switch lbSpec.Type {
case kops.LoadBalancerTypeInternal:
elb.Scheme = s("internal")
case kops.LoadBalancerTypePublic:
elb.Scheme = nil
default:
return fmt.Errorf("unknown elb Type: %q", lbSpec.Type)
}
c.AddTask(elb)
}

View File

@ -89,14 +89,47 @@ func (b *AutoscalingGroupModelBuilder) Build(c *fi.ModelBuilderContext) error {
{
associatePublicIP := true
if b.Cluster.IsTopologyPublic() {
switch ig.Spec.Role {
case kops.InstanceGroupRoleMaster:
switch b.Cluster.Spec.Topology.Masters {
case kops.TopologyPrivate:
associatePublicIP = false
// TODO: what if AssociatePublicIP is set
case kops.TopologyPublic:
associatePublicIP = true
if ig.Spec.AssociatePublicIP != nil {
associatePublicIP = *ig.Spec.AssociatePublicIP
}
default:
return fmt.Errorf("unhandled master topology %q", b.Cluster.Spec.Topology.Masters)
}
case kops.InstanceGroupRoleNode:
switch b.Cluster.Spec.Topology.Nodes {
case kops.TopologyPrivate:
associatePublicIP = false
// TODO: We probably should honor AssociatePublicIP
case kops.TopologyPublic:
associatePublicIP = true
if ig.Spec.AssociatePublicIP != nil {
associatePublicIP = *ig.Spec.AssociatePublicIP
}
default:
return fmt.Errorf("unhandled master topology %q", b.Cluster.Spec.Topology.Masters)
}
case kops.InstanceGroupRoleBastion:
associatePublicIP = true
if ig.Spec.AssociatePublicIP != nil {
associatePublicIP = *ig.Spec.AssociatePublicIP
}
}
if b.Cluster.IsTopologyPrivate() {
associatePublicIP = false
default:
return fmt.Errorf("Unknown instance group role %q", ig.Spec.Role)
}
t.AssociatePublicIP = &associatePublicIP
}

View File

@ -40,10 +40,6 @@ type BastionModelBuilder struct {
var _ fi.ModelBuilder = &BastionModelBuilder{}
func (b *BastionModelBuilder) Build(c *fi.ModelBuilderContext) error {
if !b.Cluster.IsTopologyPrivate() {
return nil
}
var bastionGroups []*kops.InstanceGroup
for _, ig := range b.InstanceGroups {
if ig.Spec.Role == kops.InstanceGroupRoleBastion {
@ -77,14 +73,6 @@ func (b *BastionModelBuilder) Build(c *fi.ModelBuilderContext) error {
c.AddTask(t)
}
//-# TODO Kris - I don't think we need to open these
//-#securityGroupRule/all-node-to-bastion:
//-# securityGroup: securityGroup/bastion.{{ ClusterName }}
//-# sourceGroup: securityGroup/nodes.{{ ClusterName }}
//-#securityGroupRule/all-master-to-bastion:
//-# securityGroup: securityGroup/bastion.{{ ClusterName }}
//-# sourceGroup: securityGroup/masters.{{ ClusterName }}
// Allow incoming SSH traffic to bastions, through the ELB
// TODO: Could we get away without an ELB here? Tricky to fix if dns-controller breaks though...
{
@ -240,10 +228,8 @@ func (b *BastionModelBuilder) Build(c *fi.ModelBuilderContext) error {
bastionPublicName = b.Cluster.Spec.Topology.Bastion.BastionPublicName
}
if bastionPublicName != "" {
// By default Bastion is not reachable from outside because of security concerns.
// But if the user specifies bastion name using edit cluster, we configure
// the bastion DNS entry for it to be reachable from outside.
// BastionPublicName --> Bastion LoadBalancer
// Here we implement the bastion CNAME logic
// By default bastions will create a CNAME that follows the `bastion-$clustername` formula
t := &awstasks.DNSName{
Name: s(bastionPublicName),
Zone: b.LinkToDNSZone(),
@ -251,6 +237,7 @@ func (b *BastionModelBuilder) Build(c *fi.ModelBuilderContext) error {
TargetLoadBalancer: elb,
}
c.AddTask(t)
}
return nil
}

View File

@ -44,6 +44,11 @@ func (b *KubeAPIServerOptionsBuilder) BuildOptions(o interface{}) error {
options.KubeAPIServer.APIServerCount = fi.Int(count)
}
if options.KubeAPIServer.StorageBackend == nil {
// For the moment, we continue to use etcd2
options.KubeAPIServer.StorageBackend = fi.String("etcd2")
}
return nil
}

View File

@ -150,8 +150,18 @@ func (m *KopsModelContext) CloudTagsForInstanceGroup(ig *kops.InstanceGroup) (ma
return labels, nil
}
func (m *KopsModelContext) UsesBastionDns() bool {
if m.Cluster.Spec.Topology.Bastion != nil && m.Cluster.Spec.Topology.Bastion.BastionPublicName != "" {
return true
}
return false
}
func (m *KopsModelContext) UseLoadBalancerForAPI() bool {
return m.Cluster.Spec.Topology.Masters == kops.TopologyPrivate
if m.Cluster.Spec.API == nil {
return false
}
return m.Cluster.Spec.API.LoadBalancer != nil
}
func (m *KopsModelContext) UsePrivateDNS() bool {

View File

@ -19,6 +19,7 @@ package model
import (
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
"strings"
)
// DNSModelBuilder builds DNS related model objects
@ -29,16 +30,29 @@ type DNSModelBuilder struct {
var _ fi.ModelBuilder = &DNSModelBuilder{}
func (b *DNSModelBuilder) Build(c *fi.ModelBuilderContext) error {
if b.UsePrivateDNS() {
// This is only exposed as a feature flag currently
// Add a HostedZone if we are going to publish a dns record that depends on it
if b.UsePrivateDNS() || b.UsesBastionDns() {
// UsePrivateDNS is only exposed as a feature flag currently
// TODO: We may still need a public zone to publish an ELB
// Check to see if we are using a bastion DNS record that points to the hosted zone
// If we are, we need to make sure we include the hosted zone as a task
// Configuration for a DNS zone, attached to our VPC
dnsZone := &awstasks.DNSZone{
Name: s(b.Cluster.Spec.DNSZone),
Name: s("private-" + b.Cluster.Spec.DNSZone),
Private: fi.Bool(true),
PrivateVPC: b.LinkToVPC(),
}
if !strings.Contains(b.Cluster.Spec.DNSZone, ".") {
// Looks like a hosted zone ID
dnsZone.ZoneID = s(b.Cluster.Spec.DNSZone)
} else {
// Looks like a normal ddns name
dnsZone.DNSName = s(b.Cluster.Spec.DNSZone)
}
c.AddTask(dnsZone)
} else if b.UseLoadBalancerForAPI() {
// This will point our DNS to the load balancer, and put the pieces
@ -49,6 +63,15 @@ func (b *DNSModelBuilder) Build(c *fi.ModelBuilderContext) error {
Name: s(b.Cluster.Spec.DNSZone),
Private: fi.Bool(false),
}
if !strings.Contains(b.Cluster.Spec.DNSZone, ".") {
// Looks like a hosted zone ID
dnsZone.ZoneID = s(b.Cluster.Spec.DNSZone)
} else {
// Looks like a normal ddns name
dnsZone.DNSName = s(b.Cluster.Spec.DNSZone)
}
c.AddTask(dnsZone)
}

View File

@ -21,7 +21,6 @@ import (
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/upup/pkg/fi/cloudup/awstasks"
"strconv"
)
// ExternalAccessModelBuilder configures security group rules for external access
@ -42,19 +41,24 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.ModelBuilderContext) error {
}
// SSH is open to AdminCIDR set
if b.Cluster.IsTopologyPublic() {
for i, sshAccess := range b.Cluster.Spec.SSHAccess {
if b.Cluster.Spec.Topology.Masters == kops.TopologyPublic {
for _, sshAccess := range b.Cluster.Spec.SSHAccess {
c.AddTask(&awstasks.SecurityGroupRule{
Name: s("ssh-external-to-master-" + strconv.Itoa(i)),
Name: s("ssh-external-to-master-" + sshAccess),
SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
Protocol: s("tcp"),
FromPort: i64(22),
ToPort: i64(22),
CIDR: s(sshAccess),
})
}
}
// SSH is open to AdminCIDR set
if b.Cluster.Spec.Topology.Nodes == kops.TopologyPublic {
for _, sshAccess := range b.Cluster.Spec.SSHAccess {
c.AddTask(&awstasks.SecurityGroupRule{
Name: s("ssh-external-to-node-" + strconv.Itoa(i)),
Name: s("ssh-external-to-node-" + sshAccess),
SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleNode),
Protocol: s("tcp"),
FromPort: i64(22),
@ -62,15 +66,17 @@ func (b *ExternalAccessModelBuilder) Build(c *fi.ModelBuilderContext) error {
CIDR: s(sshAccess),
})
}
}
if !b.UseLoadBalancerForAPI() {
// Configuration for the master, when not using a Loadbalancer (ELB)
// We expect that either the IP address is published, or DNS is set up to point to the IPs
// We need to open security groups directly to the master nodes (instead of via the ELB)
// HTTPS to the master is allowed (for API access)
for i, apiAccess := range b.Cluster.Spec.KubernetesAPIAccess {
for _, apiAccess := range b.Cluster.Spec.KubernetesAPIAccess {
t := &awstasks.SecurityGroupRule{
Name: s("https-external-to-master-" + strconv.Itoa(i)),
Name: s("https-external-to-master-" + apiAccess),
SecurityGroup: b.LinkToSecurityGroup(kops.InstanceGroupRoleMaster),
Protocol: s("tcp"),
FromPort: i64(443),

View File

@ -194,3 +194,7 @@ func (b *KopsModelContext) LinkToUtilitySubnetInZone(zoneName string) (*awstasks
func (b *KopsModelContext) NamePrivateRouteTableInZone(zoneName string) string {
return "private-" + zoneName + "." + b.ClusterName()
}
func (b *KopsModelContext) LinkToPrivateRouteTableInZone(zoneName string) *awstasks.RouteTable {
return &awstasks.RouteTable{Name: s(b.NamePrivateRouteTableInZone(zoneName))}
}

View File

@ -139,7 +139,7 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error {
// Map the Private subnet to the Private route table
c.AddTask(&awstasks.RouteTableAssociation{
Name: s("private-" + subnetSpec.Name + "." + b.ClusterName()),
RouteTable: &awstasks.RouteTable{Name: s(b.NamePrivateRouteTableInZone(subnetSpec.Zone))},
RouteTable: b.LinkToPrivateRouteTableInZone(subnetSpec.Zone),
Subnet: subnet,
})
@ -158,39 +158,48 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error {
return err
}
// Has an existing NgwId been entered?
// NGWs look like this: ngwId: nat-09c4180b76a36ca2c
ngwId := b.Cluster.Spec.Subnets[i].NgwId
// Was an elasticIp also allocated? This needs to be the ElasticIP
// associated with the NAT Gateway ngwEips look like: ngwEip: eipalloc-e1fc20df
ngwEip := b.Cluster.Spec.Subnets[i].NgwEip
// // Has an existing NgwId been entered?
// // NGWs look like this: ngwId: nat-09c4180b76a36ca2c
// ngwId := b.Cluster.Spec.Subnets[i].NgwId
// If these get triggered, something has gone wrong in pkg/apis/kops/validation.go
if ngwId != "" && ngwEip == "" {
return fmt.Errorf("must specify the associated ElasticIP when specifying NAT Gateways")
}
// // Was an elasticIp also allocated? This needs to be the ElasticIP
// // associated with the NAT Gateway ngwEips look like: ngwEip: eipalloc-e1fc20df
// ngwEip := b.Cluster.Spec.Subnets[i].NgwEip
// // If these get triggered, something has gone wrong in pkg/apis/kops/validation.go
// if ngwId != "" && ngwEip == "" {
// return fmt.Errorf("must specify the associated ElasticIP when specifying NAT Gateways")
// }
// if ngwEip != "" && ngwId == "" {
// return fmt.Errorf("must specify a NAT Gateway when specifying ElasticIP")
// }
// // Every NGW needs a public (Elastic) IP address, every private
// // subnet needs a NGW, lets create it. We tie it to a subnet
// // so we can track it in AWS
// var eip = &awstasks.ElasticIP{}
// if ngwEip == "" {
// eip = &awstasks.ElasticIP{
// Name: s(zone + "." + b.ClusterName()),
// Subnet: utilitySubnet,
// }
// } else {
// eip = &awstasks.ElasticIP{
// Name: s(zone + "." + b.ClusterName()),
// Subnet: utilitySubnet,
// ID: s(ngwEip),
// }
if ngwEip != "" && ngwId == "" {
return fmt.Errorf("must specify a NAT Gateway when specifying ElasticIP")
}
// Every NGW needs a public (Elastic) IP address, every private
// subnet needs a NGW, lets create it. We tie it to a subnet
// so we can track it in AWS
var eip = &awstasks.ElasticIP{}
if ngwEip == "" {
eip = &awstasks.ElasticIP{
Name: s(zone + "." + b.ClusterName()),
Subnet: utilitySubnet,
}
} else {
eip = &awstasks.ElasticIP{
Name: s(zone + "." + b.ClusterName()),
Subnet: utilitySubnet,
ID: s(ngwEip),
}
eip := &awstasks.ElasticIP{
Name: s(zone + "." + b.ClusterName()),
AssociatedNatGatewayRouteTable: b.LinkToPrivateRouteTableInZone(zone),
}
c.AddTask(eip)
@ -201,20 +210,29 @@ func (b *NetworkModelBuilder) Build(c *fi.ModelBuilderContext) error {
// The instances in the private subnet can access the Internet by
// using a network address translation (NAT) gateway that resides
// in the public subnet.
var ngw = &awstasks.NatGateway{}
if ngwId == "" {
ngw = &awstasks.NatGateway{
Name: s(zone + "." + b.ClusterName()),
Subnet: utilitySubnet,
ElasticIp: eip,
}
} else {
ngw = &awstasks.NatGateway{
Name: s(zone + "." + b.ClusterName()),
Subnet: utilitySubnet,
ElasticIp: eip,
ID: s(ngwId),
}
// var ngw = &awstasks.NatGateway{}
// if ngwId == "" {
// ngw = &awstasks.NatGateway{
// Name: s(zone + "." + b.ClusterName()),
// Subnet: utilitySubnet,
// ElasticIp: eip,
// }
// } else {
// ngw = &awstasks.NatGateway{
// Name: s(zone + "." + b.ClusterName()),
// Subnet: utilitySubnet,
// ElasticIp: eip,
// ID: s(ngwId),
// }
ngw := &awstasks.NatGateway{
Name: s(zone + "." + b.ClusterName()),
Subnet: utilitySubnet,
ElasticIP: eip,
AssociatedRouteTable: b.LinkToPrivateRouteTableInZone(zone),
}
c.AddTask(ngw)

View File

@ -22,7 +22,8 @@ import (
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
meta_v1 "k8s.io/kubernetes/pkg/apis/meta/v1"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/util/wait"
)
@ -41,14 +42,14 @@ const (
// TODO: should we pool the api client connection? My initial thought is no.
type NodeAPIAdapter struct {
// K8s API client this sucker talks to K8s directly - not kubectl, hard api call
client release_1_5.Interface
client k8s_clientset.Interface
//TODO: convert to arg on WaitForNodeToBe
// K8s timeout on method call
timeout time.Duration
}
func NewNodeAPIAdapter(client release_1_5.Interface, timeout time.Duration) (*NodeAPIAdapter, error) {
func NewNodeAPIAdapter(client k8s_clientset.Interface, timeout time.Duration) (*NodeAPIAdapter, error) {
if client == nil {
return nil, fmt.Errorf("client not provided")
}
@ -110,7 +111,7 @@ func (nodeAA *NodeAPIAdapter) WaitForNodeToBe(nodeName string, conditionType v1.
var cond *v1.NodeCondition
err := wait.PollImmediate(Poll, nodeAA.timeout, func() (bool, error) {
node, err := nodeAA.client.Core().Nodes().Get(nodeName)
node, err := nodeAA.client.Core().Nodes().Get(nodeName, meta_v1.GetOptions{})
// FIXME this is not erroring on 500's for instance. We will keep looping
if err != nil {
// TODO: Check if e.g. NotFound

View File

@ -21,7 +21,7 @@ import (
"time"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
)
//func TestBuildNodeAPIAdapter(t *testing.T) {

View File

@ -22,7 +22,7 @@ import (
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
)
// A cluster to validate
@ -49,7 +49,7 @@ type ValidationNode struct {
}
// ValidateCluster validate a k8s cluster with a provided instance group list
func ValidateCluster(clusterName string, instanceGroupList *kops.InstanceGroupList, clusterKubernetesClient release_1_5.Interface) (*ValidationCluster, error) {
func ValidateCluster(clusterName string, instanceGroupList *kops.InstanceGroupList, clusterKubernetesClient k8s_clientset.Interface) (*ValidationCluster, error) {
var instanceGroups []*kops.InstanceGroup
validationCluster := &ValidationCluster{}

View File

@ -22,7 +22,7 @@ import (
"k8s.io/kubernetes/pkg/api/resource"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
)
func Test_ValidateClusterPositive(t *testing.T) {

View File

@ -20,22 +20,50 @@ import (
"flag"
"fmt"
"github.com/golang/glog"
"github.com/spf13/pflag"
"k8s.io/kops/dns-controller/pkg/dns"
"k8s.io/kops/protokube/pkg/protokube"
"k8s.io/kubernetes/federation/pkg/dnsprovider"
"net"
"os"
"strings"
// Load DNS plugins
_ "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/aws/route53"
_ "k8s.io/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns"
)
var (
flags = pflag.NewFlagSet("", pflag.ExitOnError)
// value overwritten during build. This can be used to resolve issues.
BuildVersion = "0.1"
)
func main() {
fmt.Printf("protokube version %s\n", BuildVersion)
err := run()
if err != nil {
glog.Errorf("Error: %v", err)
os.Exit(1)
}
os.Exit(0)
}
func run() error {
dnsProviderId := "aws-route53"
flags.StringVar(&dnsProviderId, "dns", dnsProviderId, "DNS provider we should use (aws-route53, google-clouddns)")
var zones []string
flags.StringSliceVarP(&zones, "zone", "z", []string{}, "Configure permitted zones and their mappings")
master := false
flag.BoolVar(&master, "master", master, "Act as master")
containerized := false
flag.BoolVar(&containerized, "containerized", containerized, "Set if we are running containerized.")
dnsZoneName := ""
flag.StringVar(&dnsZoneName, "dns-zone-name", dnsZoneName, "Name of zone to use for DNS")
dnsInternalSuffix := ""
flag.StringVar(&dnsInternalSuffix, "dns-internal-suffix", dnsInternalSuffix, "DNS suffix for internal domain names")
@ -45,20 +73,24 @@ func main() {
flagChannels := ""
flag.StringVar(&flagChannels, "channels", flagChannels, "channels to install")
// Trick to avoid 'logging before flag.Parse' warning
flag.CommandLine.Parse([]string{})
flag.Set("logtostderr", "true")
flag.Parse()
flags.AddGoFlagSet(flag.CommandLine)
flags.Parse(os.Args)
volumes, err := protokube.NewAWSVolumes()
if err != nil {
glog.Errorf("Error initializing AWS: %q", err)
os.Exit(1)
return fmt.Errorf("Error initializing AWS: %q", err)
}
if clusterID == "" {
clusterID = volumes.ClusterID()
if clusterID == "" {
glog.Errorf("cluster-id is required (cannot be determined from cloud)")
os.Exit(1)
return fmt.Errorf("cluster-id is required (cannot be determined from cloud)")
} else {
glog.Infof("Setting cluster-id from cloud: %s", clusterID)
}
@ -75,11 +107,6 @@ func main() {
dnsInternalSuffix = "." + dnsInternalSuffix
}
if dnsZoneName == "" {
tokens := strings.Split(dnsInternalSuffix, ".")
dnsZoneName = strings.Join(tokens[len(tokens)-2:], ".")
}
// Get internal IP from cloud, to avoid problems if we're in a container
// TODO: Just run with --net=host ??
//internalIP, err := findInternalIP()
@ -89,10 +116,34 @@ func main() {
//}
internalIP := volumes.InternalIP()
dns, err := protokube.NewRoute53DNSProvider(dnsZoneName)
if err != nil {
glog.Errorf("Error initializing DNS: %q", err)
os.Exit(1)
var dnsScope dns.Scope
var dnsController *dns.DNSController
{
dnsProvider, err := dnsprovider.GetDnsProvider(dnsProviderId, nil)
if err != nil {
return fmt.Errorf("Error initializing DNS provider %q: %v", dnsProviderId, err)
}
if dnsProvider == nil {
return fmt.Errorf("DNS provider %q could not be initialized", dnsProviderId)
}
zoneRules, err := dns.ParseZoneRules(zones)
if err != nil {
return fmt.Errorf("unexpected zone flags: %q", err)
}
dnsController, err = dns.NewDNSController(dnsProvider, zoneRules)
if err != nil {
return err
}
dnsScope, err = dnsController.CreateScope("protokube")
if err != nil {
return err
}
// We don't really use readiness - our records are simple
dnsScope.MarkReady()
}
rootfs := "/"
@ -117,7 +168,7 @@ func main() {
//EtcdClusters : fromVolume
ModelDir: modelDir,
DNS: dns,
DNSScope: dnsScope,
Channels: channels,
@ -125,10 +176,11 @@ func main() {
}
k.Init(volumes)
go dnsController.Run()
k.RunSyncLoop()
glog.Infof("Unexpected exit")
os.Exit(1)
return fmt.Errorf("Unexpected exit")
}
// TODO: run with --net=host ??

View File

@ -1,230 +0,0 @@
/*
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 protokube
import (
"fmt"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/golang/glog"
"reflect"
"strings"
"time"
)
type Route53DNSProvider struct {
client *route53.Route53
zoneName string
zone *route53.HostedZone
}
func NewRoute53DNSProvider(zoneName string) (*Route53DNSProvider, error) {
if zoneName == "" {
return nil, fmt.Errorf("zone name is required")
}
p := &Route53DNSProvider{
zoneName: zoneName,
}
s := session.New()
s.Handlers.Send.PushFront(func(r *request.Request) {
// Log requests
glog.V(4).Infof("AWS API Request: %s/%s", r.ClientInfo.ServiceName, r.Operation.Name)
})
config := aws.NewConfig()
config = config.WithCredentialsChainVerboseErrors(true)
p.client = route53.New(s, config)
return p, nil
}
func (p *Route53DNSProvider) getZone() (*route53.HostedZone, error) {
if p.zone != nil {
return p.zone, nil
}
if !strings.Contains(p.zoneName, ".") {
// Looks like a zone ID
zoneID := p.zoneName
glog.Infof("Querying for hosted zone by id: %q", zoneID)
request := &route53.GetHostedZoneInput{
Id: aws.String(zoneID),
}
response, err := p.client.GetHostedZone(request)
if err != nil {
if AWSErrorCode(err) == "NoSuchHostedZone" {
glog.Infof("Zone not found with id %q; will reattempt by name", zoneID)
} else {
return nil, fmt.Errorf("error querying for DNS HostedZones %q: %v", zoneID, err)
}
} else {
p.zone = response.HostedZone
return p.zone, nil
}
}
glog.Infof("Querying for hosted zone by name: %q", p.zoneName)
findZone := p.zoneName
if !strings.HasSuffix(findZone, ".") {
findZone += "."
}
request := &route53.ListHostedZonesByNameInput{
DNSName: aws.String(findZone),
}
response, err := p.client.ListHostedZonesByName(request)
if err != nil {
return nil, fmt.Errorf("error querying for DNS HostedZones %q: %v", findZone, err)
}
var zones []*route53.HostedZone
for _, zone := range response.HostedZones {
if aws.StringValue(zone.Name) == findZone {
zones = append(zones, zone)
}
}
if len(zones) == 0 {
return nil, nil
}
if len(zones) != 1 {
return nil, fmt.Errorf("found multiple hosted zones matched name %q", findZone)
}
p.zone = zones[0]
return p.zone, nil
}
func (p *Route53DNSProvider) findResourceRecord(hostedZoneID string, name string, resourceType string) (*route53.ResourceRecordSet, error) {
name = strings.TrimSuffix(name, ".")
request := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String(hostedZoneID),
// TODO: Start at correct name?
}
var found *route53.ResourceRecordSet
err := p.client.ListResourceRecordSetsPages(request, func(p *route53.ListResourceRecordSetsOutput, lastPage bool) (shouldContinue bool) {
for _, rr := range p.ResourceRecordSets {
if aws.StringValue(rr.Type) != resourceType {
continue
}
rrName := aws.StringValue(rr.Name)
rrName = strings.TrimSuffix(rrName, ".")
if name == rrName {
found = rr
break
}
}
// TODO: Also exit if we are on the 'next' name?
return found == nil
})
if err != nil {
return nil, fmt.Errorf("error listing DNS ResourceRecords: %v", err)
}
if found == nil {
return nil, nil
}
return found, nil
}
func (p *Route53DNSProvider) Set(fqdn string, recordType string, value string, ttl time.Duration) error {
zone, err := p.getZone()
if err != nil {
return err
}
// More correct, and makes the simple comparisons later on work correctly
if !strings.HasSuffix(fqdn, ".") {
fqdn += "."
}
existing, err := p.findResourceRecord(aws.StringValue(zone.Id), fqdn, recordType)
if err != nil {
return err
}
rrs := &route53.ResourceRecordSet{
Name: aws.String(fqdn),
Type: aws.String(recordType),
TTL: aws.Int64(int64(ttl.Seconds())),
ResourceRecords: []*route53.ResourceRecord{
{Value: aws.String(value)},
},
}
if existing != nil {
if reflect.DeepEqual(rrs, existing) {
glog.V(2).Infof("DNS %q %s record already set to %q", fqdn, recordType, value)
return nil
} else {
glog.Infof("ResourceRecordSet change:")
glog.Infof("Existing: %v", DebugString(existing))
glog.Infof("Desired: %v", DebugString(rrs))
}
}
change := &route53.Change{
Action: aws.String("UPSERT"),
ResourceRecordSet: rrs,
}
changeBatch := &route53.ChangeBatch{}
changeBatch.Changes = []*route53.Change{change}
request := &route53.ChangeResourceRecordSetsInput{}
request.HostedZoneId = zone.Id
request.ChangeBatch = changeBatch
glog.V(2).Infof("Updating DNS record %q", fqdn)
glog.V(4).Infof("route53 request: %s", DebugString(request))
response, err := p.client.ChangeResourceRecordSets(request)
if err != nil {
return fmt.Errorf("error creating ResourceRecordSets: %v", err)
}
glog.V(2).Infof("Change id is %q", aws.StringValue(response.ChangeInfo.Id))
return nil
}
// AWSErrorCode returns the aws error code, if it is an awserr.Error, otherwise ""
func AWSErrorCode(err error) string {
if awsError, ok := err.(awserr.Error); ok {
return awsError.Code()
}
return ""
}

View File

@ -18,11 +18,11 @@ package protokube
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kops/dns-controller/pkg/dns"
"net"
"os/exec"
"time"
"github.com/golang/glog"
)
type KubeBoot struct {
@ -35,7 +35,7 @@ type KubeBoot struct {
volumeMounter *VolumeMountController
etcdControllers map[string]*EtcdController
DNS DNSProvider
DNSScope dns.Scope
ModelDir string

View File

@ -19,21 +19,21 @@ package protokube
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/client/clientset_generated/release_1_5"
k8s_clientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"sync"
)
type KubernetesContext struct {
mutex sync.Mutex
client *release_1_5.Clientset
client *k8s_clientset.Clientset
}
func NewKubernetesContext() *KubernetesContext {
return &KubernetesContext{}
}
func (c *KubernetesContext) KubernetesClient() (*release_1_5.Clientset, error) {
func (c *KubernetesContext) KubernetesClient() (*k8s_clientset.Clientset, error) {
c.mutex.Lock()
defer c.mutex.Unlock()
@ -54,7 +54,7 @@ func (c *KubernetesContext) KubernetesClient() (*release_1_5.Clientset, error) {
}
}
k8sClient, err := release_1_5.NewForConfig(clientConfig)
k8sClient, err := k8s_clientset.NewForConfig(clientConfig)
if err != nil {
return nil, fmt.Errorf("cannot build kube client: %v", err)
}

View File

@ -17,22 +17,28 @@ limitations under the License.
package protokube
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kops/dns-controller/pkg/dns"
"time"
)
const defaultTTL = time.Minute
type DNSProvider interface {
Set(fqdn string, recordType string, value string, ttl time.Duration) error
}
// CreateInternalDNSNameRecord maps a FQDN to the internal IP address of the current machine
func (k *KubeBoot) CreateInternalDNSNameRecord(fqdn string) error {
err := k.DNS.Set(fqdn, "A", k.InternalIP.String(), defaultTTL)
if err != nil {
return fmt.Errorf("error configuring DNS name %q: %v", fqdn, err)
ttl := defaultTTL
if ttl != dns.DefaultTTL {
glog.Infof("Ignoring ttl %v for %q", ttl, fqdn)
}
var records []dns.Record
records = append(records, dns.Record{
RecordType: dns.RecordTypeA,
FQDN: fqdn,
Value: k.InternalIP.String(),
})
k.DNSScope.Replace(fqdn, records)
return nil
}

View File

@ -0,0 +1,117 @@
/*
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 main
import (
"bytes"
"io/ioutil"
"k8s.io/kops/pkg/apis/kops"
"k8s.io/kops/pkg/apis/kops/v1alpha1"
"k8s.io/kops/pkg/apis/kops/v1alpha2"
"k8s.io/kops/pkg/diff"
k8sapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"path"
"testing"
_ "k8s.io/kops/pkg/apis/kops/install"
"strings"
)
// TestMinimal runs the test on a minimum configuration, similar to kops create cluster minimal.example.com --zones us-west-1a
func TestMinimal(t *testing.T) {
runTest(t, "minimal", "v1alpha1", "v1alpha2")
runTest(t, "minimal", "v1alpha2", "v1alpha1")
runTest(t, "minimal", "v1alpha0", "v1alpha1")
runTest(t, "minimal", "v1alpha0", "v1alpha2")
}
func runTest(t *testing.T, srcDir string, fromVersion string, toVersion string) {
sourcePath := path.Join(srcDir, fromVersion+".yaml")
sourceBytes, err := ioutil.ReadFile(sourcePath)
if err != nil {
t.Fatalf("unexpected error reading sourcePath %q: %v", sourcePath, err)
}
expectedPath := path.Join(srcDir, toVersion+".yaml")
expectedBytes, err := ioutil.ReadFile(expectedPath)
if err != nil {
t.Fatalf("unexpected error reading expectedPath %q: %v", expectedPath, err)
}
codec := k8sapi.Codecs.UniversalDecoder(kops.SchemeGroupVersion)
defaults := &schema.GroupVersionKind{
Group: v1alpha1.SchemeGroupVersion.Group,
Version: v1alpha1.SchemeGroupVersion.Version,
}
yaml, ok := runtime.SerializerInfoForMediaType(k8sapi.Codecs.SupportedMediaTypes(), "application/yaml")
if !ok {
t.Fatalf("no YAML serializer registered")
}
var encoder runtime.Encoder
switch toVersion {
case "v1alpha1":
encoder = k8sapi.Codecs.EncoderForVersion(yaml.Serializer, v1alpha1.SchemeGroupVersion)
case "v1alpha2":
encoder = k8sapi.Codecs.EncoderForVersion(yaml.Serializer, v1alpha2.SchemeGroupVersion)
default:
t.Fatalf("unknown version %q", toVersion)
}
//decoder := k8sapi.Codecs.DecoderToVersion(yaml.Serializer, kops.SchemeGroupVersion)
var actual []string
for _, s := range strings.Split(string(sourceBytes), "\n---\n") {
o, gvk, err := codec.Decode([]byte(s), defaults, nil)
if err != nil {
t.Fatalf("error parsing file %q: %v", sourcePath, err)
}
expectVersion := fromVersion
if expectVersion == "v1alpha0" {
// Our version before we had v1alpha1
expectVersion = "v1alpha1"
}
if gvk.Version != expectVersion {
t.Fatalf("unexpected version: %q vs %q", gvk.Version, expectVersion)
}
var b bytes.Buffer
if err := encoder.Encode(o, &b); err != nil {
t.Fatalf("error encoding object: %v", err)
}
actual = append(actual, b.String())
}
actualString := strings.TrimSpace(strings.Join(actual, "\n---\n\n"))
expectedString := strings.TrimSpace(string(expectedBytes))
if actualString != expectedString {
diffString := diff.FormatDiff(expectedString, actualString)
t.Logf("diff:\n%s\n", diffString)
t.Fatalf("converted output differed from expected")
}
}

Some files were not shown because too many files have changed in this diff Show More