github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/kubetest/kops.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"io/ioutil"
    23  	"os"
    24  	"os/exec"
    25  	"os/user"
    26  	"path/filepath"
    27  	"strconv"
    28  	"strings"
    29  	"time"
    30  )
    31  
    32  var (
    33  	// kops specific flags.
    34  	kopsPath         = flag.String("kops", "", "(kops only) Path to the kops binary. Must be set for kops.")
    35  	kopsCluster      = flag.String("kops-cluster", "", "(kops only) Cluster name. Must be set for kops.")
    36  	kopsState        = flag.String("kops-state", "", "(kops only) s3:// path to kops state store. Must be set.")
    37  	kopsSSHKey       = flag.String("kops-ssh-key", "", "(kops only) Path to ssh key-pair for each node (defaults '~/.ssh/kube_aws_rsa' if unset.)")
    38  	kopsKubeVersion  = flag.String("kops-kubernetes-version", "", "(kops only) If set, the version of Kubernetes to deploy (can be a URL to a GCS path where the release is stored) (Defaults to kops default, latest stable release.).")
    39  	kopsZones        = flag.String("kops-zones", "us-west-2a", "(kops AWS only) AWS zones for kops deployment, comma delimited.")
    40  	kopsNodes        = flag.Int("kops-nodes", 2, "(kops only) Number of nodes to create.")
    41  	kopsUpTimeout    = flag.Duration("kops-up-timeout", 20*time.Minute, "(kops only) Time limit between 'kops config / kops update' and a response from the Kubernetes API.")
    42  	kopsAdminAccess  = flag.String("kops-admin-access", "", "(kops only) If set, restrict apiserver access to this CIDR range.")
    43  	kopsImage        = flag.String("kops-image", "", "(kops only) Image (AMI) for nodes to use. (Defaults to kops default, a Debian image with a custom kubernetes kernel.)")
    44  	kopsArgs         = flag.String("kops-args", "", "(kops only) Additional space-separated args to pass unvalidated to 'kops create cluster', e.g. '--kops-args=\"--dns private --node-size t2.micro\"'")
    45  	kopsPriorityPath = flag.String("kops-priority-path", "", "Insert into PATH if set")
    46  )
    47  
    48  type kops struct {
    49  	path        string
    50  	kubeVersion string
    51  	sshKey      string
    52  	zones       []string
    53  	nodes       int
    54  	adminAccess string
    55  	cluster     string
    56  	image       string
    57  	args        string
    58  	kubecfg     string
    59  }
    60  
    61  var _ deployer = kops{}
    62  
    63  func migrateKopsEnv() error {
    64  	return migrateOptions([]migratedOption{
    65  		{
    66  			env:      "KOPS_STATE_STORE",
    67  			option:   kopsState,
    68  			name:     "--kops-state",
    69  			skipPush: true,
    70  		},
    71  		{
    72  			env:      "AWS_SSH_KEY",
    73  			option:   kopsSSHKey,
    74  			name:     "--kops-ssh-key",
    75  			skipPush: true,
    76  		},
    77  		{
    78  			env:      "PRIORITY_PATH",
    79  			option:   kopsPriorityPath,
    80  			name:     "--kops-priority-path",
    81  			skipPush: true,
    82  		},
    83  	})
    84  }
    85  
    86  func newKops() (*kops, error) {
    87  	if err := migrateKopsEnv(); err != nil {
    88  		return nil, err
    89  	}
    90  	if *kopsPath == "" {
    91  		return nil, fmt.Errorf("--kops must be set to a valid binary path for kops deployment")
    92  	}
    93  	if *kopsCluster == "" {
    94  		return nil, fmt.Errorf("--kops-cluster must be set to a valid cluster name for kops deployment")
    95  	}
    96  	if *kopsState == "" {
    97  		return nil, fmt.Errorf("--kops-state must be set to a valid S3 path for kops deployment")
    98  	}
    99  	if *kopsPriorityPath != "" {
   100  		if err := insertPath(*kopsPriorityPath); err != nil {
   101  			return nil, err
   102  		}
   103  	}
   104  
   105  	// TODO(fejta): consider explicitly passing these env items where needed.
   106  	sshKey := *kopsSSHKey
   107  	if sshKey == "" {
   108  		usr, err := user.Current()
   109  		if err != nil {
   110  			return nil, err
   111  		}
   112  		sshKey = filepath.Join(usr.HomeDir, ".ssh/kube_aws_rsa")
   113  	}
   114  	if err := os.Setenv("KOPS_STATE_STORE", *kopsState); err != nil {
   115  		return nil, err
   116  	}
   117  	f, err := ioutil.TempFile("", "kops-kubecfg")
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	defer f.Close()
   122  	kubecfg := f.Name()
   123  	if err := f.Chmod(0600); err != nil {
   124  		return nil, err
   125  	}
   126  	if err := os.Setenv("KUBECONFIG", kubecfg); err != nil {
   127  		return nil, err
   128  	}
   129  	// Set KUBERNETES_CONFORMANCE_TEST so the auth info is picked up
   130  	// from kubectl instead of bash inference.
   131  	if err := os.Setenv("KUBERNETES_CONFORMANCE_TEST", "yes"); err != nil {
   132  		return nil, err
   133  	}
   134  	// Set KUBERNETES_CONFORMANCE_PROVIDER to override the
   135  	// cloudprovider for KUBERNETES_CONFORMANCE_TEST.
   136  	if err := os.Setenv("KUBERNETES_CONFORMANCE_PROVIDER", "aws"); err != nil {
   137  		return nil, err
   138  	}
   139  	// AWS_SSH_KEY is required by the AWS e2e tests.
   140  	if err := os.Setenv("AWS_SSH_KEY", sshKey); err != nil {
   141  		return nil, err
   142  	}
   143  	// ZONE is required by the AWS e2e tests.
   144  	zones := strings.Split(*kopsZones, ",")
   145  	if err := os.Setenv("ZONE", zones[0]); err != nil {
   146  		return nil, err
   147  	}
   148  	return &kops{
   149  		path:        *kopsPath,
   150  		kubeVersion: *kopsKubeVersion,
   151  		sshKey:      sshKey + ".pub", // kops only needs the public key, e2es need the private key.
   152  		zones:       zones,
   153  		nodes:       *kopsNodes,
   154  		adminAccess: *kopsAdminAccess,
   155  		cluster:     *kopsCluster,
   156  		image:       *kopsImage,
   157  		args:        *kopsArgs,
   158  		kubecfg:     kubecfg,
   159  	}, nil
   160  }
   161  
   162  func (k kops) Up() error {
   163  	createArgs := []string{
   164  		"create", "cluster",
   165  		"--name", k.cluster,
   166  		"--ssh-public-key", k.sshKey,
   167  		"--node-count", strconv.Itoa(k.nodes),
   168  		"--zones", strings.Join(k.zones, ","),
   169  	}
   170  	if k.kubeVersion != "" {
   171  		createArgs = append(createArgs, "--kubernetes-version", k.kubeVersion)
   172  	}
   173  	if k.adminAccess != "" {
   174  		createArgs = append(createArgs, "--admin-access", k.adminAccess)
   175  	}
   176  	if k.image != "" {
   177  		createArgs = append(createArgs, "--image", k.image)
   178  	}
   179  	if k.args != "" {
   180  		createArgs = append(createArgs, strings.Split(k.args, " ")...)
   181  	}
   182  	if err := finishRunning(exec.Command(k.path, createArgs...)); err != nil {
   183  		return fmt.Errorf("kops configuration failed: %v", err)
   184  	}
   185  	if err := finishRunning(exec.Command(k.path, "update", "cluster", k.cluster, "--yes")); err != nil {
   186  		return fmt.Errorf("kops bringup failed: %v", err)
   187  	}
   188  	// TODO(zmerlynn): More cluster validation. This should perhaps be
   189  	// added to kops and not here, but this is a fine place to loop
   190  	// for now.
   191  	return waitForNodes(k, k.nodes+1, *kopsUpTimeout)
   192  }
   193  
   194  func (k kops) IsUp() error {
   195  	return isUp(k)
   196  }
   197  
   198  func (k kops) DumpClusterLogs(localPath, gcsPath string) error {
   199  	return defaultDumpClusterLogs(localPath, gcsPath)
   200  }
   201  
   202  func (k kops) TestSetup() error {
   203  	info, err := os.Stat(k.kubecfg)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	if info.Size() > 0 {
   208  		// Assume that if we already have it, it's good.
   209  		return nil
   210  	}
   211  	if err := finishRunning(exec.Command(k.path, "export", "kubecfg", k.cluster)); err != nil {
   212  		return fmt.Errorf("Failure exporting kops kubecfg: %v", err)
   213  	}
   214  	return nil
   215  }
   216  
   217  func (k kops) Down() error {
   218  	// We do a "kops get" first so the exit status of "kops delete" is
   219  	// more sensical in the case of a non-existent cluster. ("kops
   220  	// delete" will exit with status 1 on a non-existent cluster)
   221  	err := finishRunning(exec.Command(k.path, "get", "clusters", k.cluster))
   222  	if err != nil {
   223  		// This is expected if the cluster doesn't exist.
   224  		return nil
   225  	}
   226  	return finishRunning(exec.Command(k.path, "delete", "cluster", k.cluster, "--yes"))
   227  }