<commit_before>package main

import (
	"crypto/tls"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/coyove/common/sched"
	"github.com/coyove/goflyway"
	"github.com/coyove/goflyway/v"
	"golang.org/x/crypto/acme/autocert"
)

var (
	version      = "__devel__"
	remoteAddr   string
	localAddr    string
	addr         string
	httpsProxy   string
	resetTraffic bool
	cconfig      = &goflyway.ClientConfig{}
	sconfig      = &goflyway.ServerConfig{}
)

func printHelp(a ...interface{}) {
	if len(a) > 0 {
		fmt.Printf("goflyway: ")
		fmt.Println(a...)
	}
	fmt.Println("usage: goflyway -DLhHUvkqpPtTwWy address:port")
	os.Exit(0)
}

func main() {
	sched.Verbose = false

	for i, last := 1, rune(0); i < len(os.Args); i++ {
		p := strings.TrimLeft(os.Args[i], "-")

		// HACK: ss-local compatible command flags
		if p == "fast-open" || p == "V" || p == "u" || p == "m" || p == "b" {
			if i < len(os.Args)-1 && !strings.HasPrefix(os.Args[i+1], "-") {
				i++
			}
			continue
		}

		if len(p) != len(os.Args[i]) {
			for i, c := range p {
				switch c {
				case 'h':
					printHelp()
				//case 'V':
				//	printHelp(version)
				case 'L', 'P', 'p', 'k', 't', 'T', 'W', 'H', 'U', 'D', 'c':
					last = c
				case 'v':
					v.Verbose++
				case 'q':
					v.Verbose = -1
				case 'w':
					cconfig.WebSocket = true
				case 'y':
					resetTraffic = true
				case '=':
					i++
					fallthrough
				default:
					if last == 0 {
						printHelp("illegal option --", string(c))
					}
					p = p[i:]
					goto PARSE
				}
			}
			continue
		}
	PARSE:
		if strings.HasPrefix(p, "\"") {
			if p, _ = strconv.Unquote(p); p == "" {
				printHelp("illegal option --", string(last))
			}
		}
		switch last {
		case 'D':
			cconfig.Dynamic = true
			fallthrough
		case 'L':
			switch parts := strings.Split(p, ":"); len(parts) {
			case 1:
				localAddr = ":" + parts[0]
			case 2:
				localAddr = p
			case 3:
				localAddr, remoteAddr = ":"+parts[0], parts[1]+":"+parts[2]
			case 4:
				localAddr, remoteAddr = parts[0]+":"+parts[1], parts[2]+":"+parts[3]
			default:
				printHelp("illegal option --", string(last), p)
			}
		case 'P':
			sconfig.ProxyPassAddr = p
		case 'U':
			cconfig.PathPattern = p
		case 'T':
			speed, _ := strconv.ParseInt(p, 10, 64)
			sconfig.SpeedThrot = goflyway.NewTokenBucket(speed, speed*25)
		case 'W':
			writebuffer, _ := strconv.ParseInt(p, 10, 64)
			sconfig.WriteBuffer, cconfig.WriteBuffer = writebuffer, writebuffer
		case 't':
			*(*int64)(&cconfig.Timeout), _ = strconv.ParseInt(p+"000000000", 10, 64)
			sconfig.Timeout = cconfig.Timeout
		case 'p', 'k':
			sconfig.Key, cconfig.Key = p, p
		case 'H':
			cconfig.URLHeader = p
			httpsProxy = p
		case 'c':
			buf, _ := ioutil.ReadFile(p)
			cmds := make(map[string]interface{})
			json.Unmarshal(buf, &cmds)
			cconfig.Key, cconfig.VPN = cmds["password"].(string), true
			addr = fmt.Sprintf("%v:%v", cmds["server"], cmds["server_port"])

			v.Verbose = 3
			v.Vprint(os.Args, " config: ", cmds)
		default:
			addr = p
		}
		last = 0
	}

	if addr == "" {
		if localAddr == "" {
			v.Vprint("assume you want a default server at :8100")
			addr = ":8100"
		} else {
			printHelp("missing address:port to listen/connect")
		}
	}

	if localAddr != "" && remoteAddr == "" {
		_, port, err1 := net.SplitHostPort(localAddr)
		host, _, err2 := net.SplitHostPort(addr)
		remoteAddr = host + ":" + port
		if err1 != nil || err2 != nil {
			printHelp("invalid address --", localAddr, addr)
		}
	}

	if localAddr != "" && remoteAddr != "" {
		cconfig.Bind = remoteAddr
		cconfig.Upstream = addr
		cconfig.Stat = &goflyway.Traffic{}

		if v.Verbose > 0 {
			go watchTraffic(cconfig, resetTraffic)
		}
		if cconfig.Dynamic {
			v.Vprint("dynamic: forward ", localAddr, " to * through ", addr)
		} else {
			v.Vprint("forward ", localAddr, " to ", remoteAddr, " through ", addr)
		}
		if cconfig.WebSocket {
			v.Vprint("relay: use Websocket protocol")
		}
		if a := os.Getenv("http_proxy") + os.Getenv("HTTP_PROXY"); a != "" {
			v.Vprint("note: system HTTP proxy is set to: ", a)
		}
		if a := os.Getenv("https_proxy") + os.Getenv("HTTPS_PROXY"); a != "" {
			v.Vprint("note: system HTTPS proxy is set to: ", a)
		}

		v.Eprint(goflyway.NewClient(localAddr, cconfig))
	} else if httpsProxy != "" {
		v.Vprint("server listen on ", addr, " (https://", httpsProxy, ")")
		m := &autocert.Manager{
			Cache:      autocert.DirCache("secret-dir"),
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(httpsProxy),
		}
		s := &http.Server{
			Addr:         addr,
			TLSConfig:    m.TLSConfig(),
			TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
		}
		s.Handler = &connector{}
		v.Eprint(s.ListenAndServeTLS("", ""))
	} else {
		v.Vprint("server listen on ", addr)
		v.Eprint(goflyway.NewServer(addr, sconfig))
	}
}

func watchTraffic(cconfig *goflyway.ClientConfig, reset bool) {
	path := filepath.Join(os.TempDir(), "goflyway_traffic")

	tmpbuf, _ := ioutil.ReadFile(path)
	if len(tmpbuf) != 16 || reset {
		tmpbuf = make([]byte, 16)
	}

	cconfig.Stat.Set(int64(binary.BigEndian.Uint64(tmpbuf)), int64(binary.BigEndian.Uint64(tmpbuf[8:])))

	var lastSent, lastRecv int64
	for range time.Tick(time.Second * 5) {
		s, r := *cconfig.Stat.Sent(), *cconfig.Stat.Recv()
		sv, rv := float64(s-lastSent)/1024/1024/5, float64(r-lastRecv)/1024/1024/5
		lastSent, lastRecv = s, r

		if sv >= 0.001 || rv >= 0.001 {
			v.Vprint("client send: ", float64(s)/1024/1024, "M (", sv, "M/s), recv: ", float64(r)/1024/1024, "M (", rv, "M/s)")
		}

		binary.BigEndian.PutUint64(tmpbuf, uint64(s))
		binary.BigEndian.PutUint64(tmpbuf[8:], uint64(r))
		ioutil.WriteFile(path, tmpbuf, 0644)
	}
}

type connector struct{}

func (c *connector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != "CONNECT" {
		w.WriteHeader(404)
		return
	}

	// we are inside GFW and should pass data to upstream
	host := r.URL.Host
	v.VVprint(r.URL)

	if !regexp.MustCompile(`:\d+$`).MatchString(host) {
		host += ":443"
	}

	up, err := net.Dial("tcp", host)
	if err != nil {
		v.Eprint(err)
		w.WriteHeader(500)
		return
	}

	hij, _ := w.(http.Hijacker) // No HTTP2
	proxyClient, _, err := hij.Hijack()
	if err != nil {
		v.Eprint(err)
		w.WriteHeader(500)
		return
	}

	proxyClient.Write([]byte("HTTP/1.1 101 Switching Protocols"))

	go func() {
		if _, err := io.Copy(proxyClient, up); err != nil {
			v.Eprint(err)
		}
	}()

	go func() {
		if _, err := io.Copy(up, proxyClient); err != nil {
			v.Eprint(err)
		}
	}()
}
<commit_msg>Disable http2<commit_after>package main

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/coyove/common/sched"
	"github.com/coyove/goflyway"
	"github.com/coyove/goflyway/v"
	"golang.org/x/crypto/acme/autocert"
)

var (
	version      = "__devel__"
	remoteAddr   string
	localAddr    string
	addr         string
	httpsProxy   string
	resetTraffic bool
	cconfig      = &goflyway.ClientConfig{}
	sconfig      = &goflyway.ServerConfig{}
)

func printHelp(a ...interface{}) {
	if len(a) > 0 {
		fmt.Printf("goflyway: ")
		fmt.Println(a...)
	}
	fmt.Println("usage: goflyway -DLhHUvkqpPtTwWy address:port")
	os.Exit(0)
}

func main() {
	sched.Verbose = false

	for i, last := 1, rune(0); i < len(os.Args); i++ {
		p := strings.TrimLeft(os.Args[i], "-")

		// HACK: ss-local compatible command flags
		if p == "fast-open" || p == "V" || p == "u" || p == "m" || p == "b" {
			if i < len(os.Args)-1 && !strings.HasPrefix(os.Args[i+1], "-") {
				i++
			}
			continue
		}

		if len(p) != len(os.Args[i]) {
			for i, c := range p {
				switch c {
				case 'h':
					printHelp()
				//case 'V':
				//	printHelp(version)
				case 'L', 'P', 'p', 'k', 't', 'T', 'W', 'H', 'U', 'D', 'c':
					last = c
				case 'v':
					v.Verbose++
				case 'q':
					v.Verbose = -1
				case 'w':
					cconfig.WebSocket = true
				case 'y':
					resetTraffic = true
				case '=':
					i++
					fallthrough
				default:
					if last == 0 {
						printHelp("illegal option --", string(c))
					}
					p = p[i:]
					goto PARSE
				}
			}
			continue
		}
	PARSE:
		if strings.HasPrefix(p, "\"") {
			if p, _ = strconv.Unquote(p); p == "" {
				printHelp("illegal option --", string(last))
			}
		}
		switch last {
		case 'D':
			cconfig.Dynamic = true
			fallthrough
		case 'L':
			switch parts := strings.Split(p, ":"); len(parts) {
			case 1:
				localAddr = ":" + parts[0]
			case 2:
				localAddr = p
			case 3:
				localAddr, remoteAddr = ":"+parts[0], parts[1]+":"+parts[2]
			case 4:
				localAddr, remoteAddr = parts[0]+":"+parts[1], parts[2]+":"+parts[3]
			default:
				printHelp("illegal option --", string(last), p)
			}
		case 'P':
			sconfig.ProxyPassAddr = p
		case 'U':
			cconfig.PathPattern = p
		case 'T':
			speed, _ := strconv.ParseInt(p, 10, 64)
			sconfig.SpeedThrot = goflyway.NewTokenBucket(speed, speed*25)
		case 'W':
			writebuffer, _ := strconv.ParseInt(p, 10, 64)
			sconfig.WriteBuffer, cconfig.WriteBuffer = writebuffer, writebuffer
		case 't':
			*(*int64)(&cconfig.Timeout), _ = strconv.ParseInt(p+"000000000", 10, 64)
			sconfig.Timeout = cconfig.Timeout
		case 'p', 'k':
			sconfig.Key, cconfig.Key = p, p
		case 'H':
			cconfig.URLHeader = p
			httpsProxy = p
		case 'c':
			buf, _ := ioutil.ReadFile(p)
			cmds := make(map[string]interface{})
			json.Unmarshal(buf, &cmds)
			cconfig.Key, cconfig.VPN = cmds["password"].(string), true
			addr = fmt.Sprintf("%v:%v", cmds["server"], cmds["server_port"])

			v.Verbose = 3
			v.Vprint(os.Args, " config: ", cmds)
		default:
			addr = p
		}
		last = 0
	}

	if addr == "" {
		if localAddr == "" {
			v.Vprint("assume you want a default server at :8100")
			addr = ":8100"
		} else {
			printHelp("missing address:port to listen/connect")
		}
	}

	if localAddr != "" && remoteAddr == "" {
		_, port, err1 := net.SplitHostPort(localAddr)
		host, _, err2 := net.SplitHostPort(addr)
		remoteAddr = host + ":" + port
		if err1 != nil || err2 != nil {
			printHelp("invalid address --", localAddr, addr)
		}
	}

	if localAddr != "" && remoteAddr != "" {
		cconfig.Bind = remoteAddr
		cconfig.Upstream = addr
		cconfig.Stat = &goflyway.Traffic{}

		if v.Verbose > 0 {
			go watchTraffic(cconfig, resetTraffic)
		}
		if cconfig.Dynamic {
			v.Vprint("dynamic: forward ", localAddr, " to * through ", addr)
		} else {
			v.Vprint("forward ", localAddr, " to ", remoteAddr, " through ", addr)
		}
		if cconfig.WebSocket {
			v.Vprint("relay: use Websocket protocol")
		}
		if a := os.Getenv("http_proxy") + os.Getenv("HTTP_PROXY"); a != "" {
			v.Vprint("note: system HTTP proxy is set to: ", a)
		}
		if a := os.Getenv("https_proxy") + os.Getenv("HTTPS_PROXY"); a != "" {
			v.Vprint("note: system HTTPS proxy is set to: ", a)
		}

		v.Eprint(goflyway.NewClient(localAddr, cconfig))
	} else if httpsProxy != "" {
		v.Vprint("server listen on ", addr, " (https://", httpsProxy, ")")
		m := &autocert.Manager{
			Cache:      autocert.DirCache("secret-dir"),
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(httpsProxy),
		}
		s := &http.Server{
			Addr:      addr,
			TLSConfig: m.TLSConfig(),
		}
		for i, p := range s.TLSConfig.NextProtos {
			if p == "h2" {
				s.TLSConfig.NextProtos[i] = "h2-disabled"
			}
		}
		s.Handler = &connector{}
		v.Eprint(s.ListenAndServeTLS("", ""))
	} else {
		v.Vprint("server listen on ", addr)
		v.Eprint(goflyway.NewServer(addr, sconfig))
	}
}

func watchTraffic(cconfig *goflyway.ClientConfig, reset bool) {
	path := filepath.Join(os.TempDir(), "goflyway_traffic")

	tmpbuf, _ := ioutil.ReadFile(path)
	if len(tmpbuf) != 16 || reset {
		tmpbuf = make([]byte, 16)
	}

	cconfig.Stat.Set(int64(binary.BigEndian.Uint64(tmpbuf)), int64(binary.BigEndian.Uint64(tmpbuf[8:])))

	var lastSent, lastRecv int64
	for range time.Tick(time.Second * 5) {
		s, r := *cconfig.Stat.Sent(), *cconfig.Stat.Recv()
		sv, rv := float64(s-lastSent)/1024/1024/5, float64(r-lastRecv)/1024/1024/5
		lastSent, lastRecv = s, r

		if sv >= 0.001 || rv >= 0.001 {
			v.Vprint("client send: ", float64(s)/1024/1024, "M (", sv, "M/s), recv: ", float64(r)/1024/1024, "M (", rv, "M/s)")
		}

		binary.BigEndian.PutUint64(tmpbuf, uint64(s))
		binary.BigEndian.PutUint64(tmpbuf[8:], uint64(r))
		ioutil.WriteFile(path, tmpbuf, 0644)
	}
}

type connector struct{}

func (c *connector) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	plain := false

	if r.Method != "CONNECT" {
		if r.URL.Host == "" {
			w.WriteHeader(404)
			return
		}

		v.VVprint("plain http proxy: ", r.URL)
		plain = true
	}

	// we are inside GFW and should pass data to upstream
	host := r.URL.Host
	if !regexp.MustCompile(`:\d+$`).MatchString(host) {
		if plain {
			host += ":80"
		} else {
			host += ":443"
		}
	}

	up, err := net.Dial("tcp", host)
	if err != nil {
		v.Eprint(err)
		w.WriteHeader(500)
		return
	}

	hij, _ := w.(http.Hijacker) // No HTTP2
	proxyClient, _, err := hij.Hijack()
	if err != nil {
		v.Eprint(err)
		w.WriteHeader(500)
		return
	}

	if plain {
		req, _ := httputil.DumpRequestOut(r, false)
		io.Copy(up, io.MultiReader(bytes.NewReader(req), r.Body))
	} else {
		proxyClient.Write([]byte("HTTP/1.0 200 Connection Established\r\n\r\n"))
	}

	go func() {
		wait := make(chan bool)
		go func() {
			if _, err := io.Copy(proxyClient, up); err != nil {
				v.Eprint(err)
			}
			wait <- true
		}()
		if _, err := io.Copy(up, proxyClient); err != nil {
			v.Eprint(err)
		}
		select {
		case <-wait:
		}
		proxyClient.Close()
		up.Close()
	}()
}
<|endoftext|>


<commit_before>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
The gomote command is a client for the Go builder infrastructure.
It's a remote control for remote Go builder machines.

See https://golang.org/wiki/Gomote

Usage:

	gomote [global-flags] cmd [cmd-flags]

	For example,
	$ gomote create openbsd-amd64-60
	user-username-openbsd-amd64-60-0
	$ gomote push user-username-openbsd-amd64-60-0
	$ gomote run user-username-openbsd-amd64-60-0 go/src/make.bash
	$ gomote run user-username-openbsd-amd64-60-0 go/bin/go test -v -short os

To list the subcommands, run "gomote" without arguments:

	Commands:

	  create     create a buildlet; with no args, list types of buildlets
	  destroy    destroy a buildlet
	  gettar     extract a tar.gz from a buildlet
	  list       list active buildlets
	  ls         list the contents of a directory on a buildlet
	  ping       test whether a buildlet is alive and reachable
	  push       sync your GOROOT directory to the buildlet
	  put        put files on a buildlet
	  put14      put Go 1.4 in place
	  puttar     extract a tar.gz to a buildlet
	  rm         delete files or directories
	  rdp        RDP (Remote Desktop Protocol) to a Windows buildlet
	  run        run a command on a buildlet
	  ssh        ssh to a buildlet

To list all the builder types available, run "create" with no arguments:

	$ gomote create
	(list tons of buildlet types)

The "gomote run" command has many of its own flags:

	$ gomote run -h
	run usage: gomote run [run-opts] <instance> <cmd> [args...]
	  -builderenv string
	        Optional alternate builder to act like. Must share the same
	        underlying buildlet host type, or it's an error. For
	        instance, linux-amd64-race is compatible
	        with linux-amd64, but openbsd-amd64 and openbsd-386 are
	        different hosts.
	  -debug
	        write debug info about the command's execution before it begins
	  -dir string
	        Directory to run from. Defaults to the directory of the
	        command, or the work directory if -system is true.
	  -e value
	        Environment variable KEY=value. The -e flag may be repeated
	        multiple times to add multiple things to the environment.
	  -path string
	        Comma-separated list of ExecOpts.Path elements. The special
	        string 'EMPTY' means to run without any $PATH. The empty
	        string (default) does not modify the $PATH. Otherwise, the
	        following expansions apply: the string '$PATH' expands to
	        the current PATH element(s), the substring '$WORKDIR'
	        expands to the buildlet's temp workdir.
	  -system
	        run inside the system, and not inside the workdir; this is implicit if cmd starts with '/'

# Debugging buildlets directly

Using "gomote create" contacts the build coordinator
(farmer.golang.org) and requests that it create the buildlet on your
behalf. All subsequent commands (such as "gomote run" or "gomote ls")
then proxy your request via the coordinator.  To access a buildlet
directly (for example, when working on the buildlet code), you can
skip the "gomote create" step and use the special builder name
"<build-config-name>@ip[:port>", such as "windows-amd64-2008@10.1.5.3".

## Groups

Instances may be managed in named groups, and commands are broadcast to all
instances in the group.

A group is specified either by the -group global flag or through the
GOMOTE_GROUP environment variable. The -group flag must always specify a
valid group, whereas GOMOTE_GROUP may contain an invalid group.
Instances may be part of more than one group.

Groups may be explicitly managed with the "group" subcommand, but there
are several short-cuts that make this unnecessary in most cases:

- The create command can create a new group for instances with the
  -new-group flag.
- The create command will automatically create the group in GOMOTE_GROUP
  if it does not exist and no other group is explicitly specified.
- The destroy command can destroy a group in addition to its instances
  with the -destroy-group flag.

As a result, the easiest way to use groups is to just set the
GOMOTE_GROUP environment variable:

	$ export GOMOTE_GROUP=debug
	$ gomote create linux-amd64
	$ GOROOT=/path/to/goroot gomote create linux-amd64
	$ gomote run go/src/make.bash

As this example demonstrates, groups are useful even if the group
contains only a single instance: it can dramatically shorten most gomote
commands.

## Tips and tricks

- The create command accepts the -setup flag which also pushes a GOROOT
  and runs the appropriate equivalent of "make.bash" for the instance.
- The create command accepts the -count flag for creating several
  instances at once.
- The run command accepts the -collect flag for automatically writing
  the output from the command to a file in $PWD, as well as a copy of
  the full file tree from the instance. This command is useful for
  capturing the output of long-running commands in a set-and-forget
  manner.
- The run command accepts the -until flag for continuously executing
  a command until the output of the command matches some pattern. Useful
  for reproducing rare issues, and especially useful when used in tandem
  with -collect.
- The run command always streams output to a temporary file regardless
  of any additional flags to avoid losing output due to terminal
  scrollback. It always prints the location of the file.

Using some of these tricks, it's straightforward to hammer at some test
to reproduce a rare failure, like so:

	$ export GOMOTE_GROUP=debug
	$ GOROOT=/path/to/goroot gomote create -setup -count=10 linux-amd64
	$ gomote run -until='unexpected return pc' -collect go/bin/go run -run="MyFlakyTest" -count=100 runtime

# Recent breaking CLI changes

- gettar writes to <instance name>.tar.gz by default, not to stdout.
- puttar has a new CLI that accepts a wider range of sources as an
  argument, removing a few mutually exclusive flags.
- More output is now emitted, but those = lines always start with "#"
  so they're fairly easy to filter out.

*/
package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"sort"
	"strconv"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
	"golang.org/x/build/internal/gomote/protos"
	"golang.org/x/build/internal/iapclient"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var (
	buildEnv    *buildenv.Environment
	activeGroup *groupData
)

type command struct {
	name string
	des  string
	run  func([]string) error
}

var commands = map[string]command{}

func sortedCommands() []string {
	s := make([]string, 0, len(commands))
	for name := range commands {
		s = append(s, name)
	}
	sort.Strings(s)
	return s
}

func usage() {
	fmt.Fprintf(os.Stderr, `Usage of gomote: gomote [global-flags] <cmd> [cmd-flags]

Global flags:
`)
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "Commands:\n\n")
	for _, name := range sortedCommands() {
		fmt.Fprintf(os.Stderr, "  %-13s %s\n", name, commands[name].des)
	}
	os.Exit(1)
}

func registerCommand(name, des string, run func([]string) error) {
	if _, dup := commands[name]; dup {
		panic("duplicate registration of " + name)
	}
	commands[name] = command{
		name: name,
		des:  des,
		run:  run,
	}
}

func registerCommands(version int) {
	if version == 2 {
		registerCommand("create", "create a buildlet; with no args, list types of buildlets", create)
		registerCommand("destroy", "destroy a buildlet", destroy)
		registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
		registerCommand("group", "manage groups of instances", group)
		registerCommand("ls", "list the contents of a directory on a buildlet", ls)
		registerCommand("list", "list active buildlets", list)
		registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
		registerCommand("push", "sync your GOROOT directory to the buildlet", push)
		registerCommand("put", "put files on a buildlet", put)
		registerCommand("putbootstrap", "put bootstrap toolchain in place", putBootstrap)
		registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
		registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
		registerCommand("rm", "delete files or directories", rm)
		registerCommand("run", "run a command on a buildlet", run)
		registerCommand("ssh", "ssh to a buildlet", ssh)
		return
	}
	registerCommand("create", "create a buildlet; with no args, list types of buildlets", legacyCreate)
	registerCommand("destroy", "destroy a buildlet", legacyDestroy)
	registerCommand("gettar", "extract a tar.gz from a buildlet", legacyGetTar)
	registerCommand("ls", "list the contents of a directory on a buildlet", legacyLs)
	registerCommand("list", "list active buildlets", legacyList)
	registerCommand("ping", "test whether a buildlet is alive and reachable ", legacyPing)
	registerCommand("push", "sync your GOROOT directory to the buildlet", legacyPush)
	registerCommand("put", "put files on a buildlet", legacyPut)
	registerCommand("put14", "put Go 1.4 in place", put14)
	registerCommand("puttar", "extract a tar.gz to a buildlet", legacyPutTar)
	registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
	registerCommand("rm", "delete files or directories", legacyRm)
	registerCommand("run", "run a command on a buildlet", legacyRun)
	registerCommand("group", "manage gomote groups (v2 only)", group)
	registerCommand("ssh", "ssh to a buildlet", legacySSH)
}

var (
	serverAddr = flag.String("server", "build.golang.org:443", "Address for GRPC server")
)

func main() {
	// Set up and parse global flags.
	groupName := flag.String("group", os.Getenv("GOMOTE_GROUP"), "name of the gomote group to apply commands to (default is $GOMOTE_GROUP)")
	buildlet.RegisterFlags()
	version := 2
	if vs := os.Getenv("GOMOTE_VERSION"); vs != "" {
		v, err := strconv.Atoi(vs)
		if err == nil {
			version = v
		}
	}
	if version < 1 || version > 2 {
		fmt.Fprintf(os.Stderr, "unsupported version %d", version)
	}
	registerCommands(version)
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}

	// Set up globals.
	buildEnv = buildenv.FromFlags()
	if *groupName != "" {
		var err error
		activeGroup, err = loadGroup(*groupName)
		if os.Getenv("GOMOTE_GROUP") != *groupName {
			// Only fail hard since it was specified by the flag.
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failure: %v\n", err)
				usage()
			}
		} else {
			// With a valid group from GOMOTE_GROUP,
			// make it explicit to the user that we're going
			// ahead with it. We don't need this with the flag
			// because it's explicit.
			if err == nil {
				fmt.Fprintf(os.Stderr, "# Using group %q from GOMOTE_GROUP\n", *groupName)
			}
			// Note that an invalid group in GOMOTE_GROUP is OK.
		}
	}

	cmdName := args[0]
	cmd, ok := commands[cmdName]
	if !ok {
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmdName)
		usage()
	}
	if err := cmd.run(args[1:]); err != nil {
		logAndExitf("Error running %s: %v\n", cmdName, err)
	}
}

// gomoteServerClient returns a gomote server client which can be used to interact with the gomote GRPC server.
// It will either retrieve a previously created authentication token or attempt to create a new one.
func gomoteServerClient(ctx context.Context) protos.GomoteServiceClient {
	grpcClient, err := iapclient.GRPCClient(ctx, *serverAddr)
	if err != nil {
		logAndExitf("dialing the server=%s failed with: %s", *serverAddr, err)
	}
	return protos.NewGomoteServiceClient(grpcClient)
}

// logAndExitf is equivalent to Printf to Stderr followed by a call to os.Exit(1).
func logAndExitf(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, format, v...)
	os.Exit(1)
}

// statusFromError returns the message portion of a GRPC error.
func statusFromError(err error) string {
	return status.Convert(err).Message()
}

func instanceDoesNotExist(err error) bool {
	return status.Code(err) == codes.NotFound
}
<commit_msg>gomote: clean up recently added documentation<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
The gomote command is a client for the Go builder infrastructure.
It's a remote control for remote Go builder machines.

See https://golang.org/wiki/Gomote

Usage:

	gomote [global-flags] cmd [cmd-flags]

	For example,
	$ gomote create openbsd-amd64-60
	user-username-openbsd-amd64-60-0
	$ gomote push user-username-openbsd-amd64-60-0
	$ gomote run user-username-openbsd-amd64-60-0 go/src/make.bash
	$ gomote run user-username-openbsd-amd64-60-0 go/bin/go test -v -short os

To list the subcommands, run "gomote" without arguments:

	Commands:

	  create     create a buildlet; with no args, list types of buildlets
	  destroy    destroy a buildlet
	  gettar     extract a tar.gz from a buildlet
	  list       list active buildlets
	  ls         list the contents of a directory on a buildlet
	  ping       test whether a buildlet is alive and reachable
	  push       sync your GOROOT directory to the buildlet
	  put        put files on a buildlet
	  put14      put Go 1.4 in place
	  puttar     extract a tar.gz to a buildlet
	  rm         delete files or directories
	  rdp        RDP (Remote Desktop Protocol) to a Windows buildlet
	  run        run a command on a buildlet
	  ssh        ssh to a buildlet

To list all the builder types available, run "create" with no arguments:

	$ gomote create
	(list tons of buildlet types)

The "gomote run" command has many of its own flags:

	$ gomote run -h
	run usage: gomote run [run-opts] <instance> <cmd> [args...]
	  -builderenv string
	        Optional alternate builder to act like. Must share the same
	        underlying buildlet host type, or it's an error. For
	        instance, linux-amd64-race is compatible
	        with linux-amd64, but openbsd-amd64 and openbsd-386 are
	        different hosts.
	  -debug
	        write debug info about the command's execution before it begins
	  -dir string
	        Directory to run from. Defaults to the directory of the
	        command, or the work directory if -system is true.
	  -e value
	        Environment variable KEY=value. The -e flag may be repeated
	        multiple times to add multiple things to the environment.
	  -path string
	        Comma-separated list of ExecOpts.Path elements. The special
	        string 'EMPTY' means to run without any $PATH. The empty
	        string (default) does not modify the $PATH. Otherwise, the
	        following expansions apply: the string '$PATH' expands to
	        the current PATH element(s), the substring '$WORKDIR'
	        expands to the buildlet's temp workdir.
	  -system
	        run inside the system, and not inside the workdir; this is implicit if cmd starts with '/'

# Debugging buildlets directly

Using "gomote create" contacts the build coordinator
(farmer.golang.org) and requests that it create the buildlet on your
behalf. All subsequent commands (such as "gomote run" or "gomote ls")
then proxy your request via the coordinator.  To access a buildlet
directly (for example, when working on the buildlet code), you can
skip the "gomote create" step and use the special builder name
"<build-config-name>@ip[:port>", such as "windows-amd64-2008@10.1.5.3".

# Groups

Instances may be managed in named groups, and commands are broadcast to all
instances in the group.

A group is specified either by the -group global flag or through the
GOMOTE_GROUP environment variable. The -group flag must always specify a
valid group, whereas GOMOTE_GROUP may contain an invalid group.
Instances may be part of more than one group.

Groups may be explicitly managed with the "group" subcommand, but there
are several short-cuts that make this unnecessary in most cases:

- The create command can create a new group for instances with the
  -new-group flag.
- The create command will automatically create the group in GOMOTE_GROUP
  if it does not exist and no other group is explicitly specified.
- The destroy command can destroy a group in addition to its instances
  with the -destroy-group flag.

As a result, the easiest way to use groups is to just set the
GOMOTE_GROUP environment variable:

	$ export GOMOTE_GROUP=debug
	$ gomote create linux-amd64
	$ GOROOT=/path/to/goroot gomote create linux-amd64
	$ gomote run go/src/make.bash

As this example demonstrates, groups are useful even if the group
contains only a single instance: it can dramatically shorten most gomote
commands.

# Tips and tricks

- The create command accepts the -setup flag which also pushes a GOROOT
  and runs the appropriate equivalent of "make.bash" for the instance.
- The create command accepts the -count flag for creating several
  instances at once.
- The run command accepts the -collect flag for automatically writing
  the output from the command to a file in $PWD, as well as a copy of
  the full file tree from the instance. This command is useful for
  capturing the output of long-running commands in a set-and-forget
  manner.
- The run command accepts the -until flag for continuously executing
  a command until the output of the command matches some pattern. Useful
  for reproducing rare issues, and especially useful when used in tandem
  with -collect.
- The run command always streams output to a temporary file regardless
  of any additional flags to avoid losing output due to terminal
  scrollback. It always prints the location of the file.

Using some of these tricks, it's straightforward to hammer at some test
to reproduce a rare failure, like so:

	$ export GOMOTE_GROUP=debug
	$ GOROOT=/path/to/goroot gomote create -setup -count=10 linux-amd64
	$ gomote run -until='unexpected return pc' -collect go/bin/go run -run="MyFlakyTest" -count=100 runtime

*/
package main

import (
	"context"
	"flag"
	"fmt"
	"os"
	"sort"
	"strconv"

	"golang.org/x/build/buildenv"
	"golang.org/x/build/buildlet"
	"golang.org/x/build/internal/gomote/protos"
	"golang.org/x/build/internal/iapclient"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

var (
	buildEnv    *buildenv.Environment
	activeGroup *groupData
)

type command struct {
	name string
	des  string
	run  func([]string) error
}

var commands = map[string]command{}

func sortedCommands() []string {
	s := make([]string, 0, len(commands))
	for name := range commands {
		s = append(s, name)
	}
	sort.Strings(s)
	return s
}

func usage() {
	fmt.Fprintf(os.Stderr, `Usage of gomote: gomote [global-flags] <cmd> [cmd-flags]

Global flags:
`)
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "Commands:\n\n")
	for _, name := range sortedCommands() {
		fmt.Fprintf(os.Stderr, "  %-13s %s\n", name, commands[name].des)
	}
	os.Exit(1)
}

func registerCommand(name, des string, run func([]string) error) {
	if _, dup := commands[name]; dup {
		panic("duplicate registration of " + name)
	}
	commands[name] = command{
		name: name,
		des:  des,
		run:  run,
	}
}

func registerCommands(version int) {
	if version == 2 {
		registerCommand("create", "create a buildlet; with no args, list types of buildlets", create)
		registerCommand("destroy", "destroy a buildlet", destroy)
		registerCommand("gettar", "extract a tar.gz from a buildlet", getTar)
		registerCommand("group", "manage groups of instances", group)
		registerCommand("ls", "list the contents of a directory on a buildlet", ls)
		registerCommand("list", "list active buildlets", list)
		registerCommand("ping", "test whether a buildlet is alive and reachable ", ping)
		registerCommand("push", "sync your GOROOT directory to the buildlet", push)
		registerCommand("put", "put files on a buildlet", put)
		registerCommand("putbootstrap", "put bootstrap toolchain in place", putBootstrap)
		registerCommand("puttar", "extract a tar.gz to a buildlet", putTar)
		registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
		registerCommand("rm", "delete files or directories", rm)
		registerCommand("run", "run a command on a buildlet", run)
		registerCommand("ssh", "ssh to a buildlet", ssh)
		return
	}
	registerCommand("create", "create a buildlet; with no args, list types of buildlets", legacyCreate)
	registerCommand("destroy", "destroy a buildlet", legacyDestroy)
	registerCommand("gettar", "extract a tar.gz from a buildlet", legacyGetTar)
	registerCommand("ls", "list the contents of a directory on a buildlet", legacyLs)
	registerCommand("list", "list active buildlets", legacyList)
	registerCommand("ping", "test whether a buildlet is alive and reachable ", legacyPing)
	registerCommand("push", "sync your GOROOT directory to the buildlet", legacyPush)
	registerCommand("put", "put files on a buildlet", legacyPut)
	registerCommand("put14", "put Go 1.4 in place", put14)
	registerCommand("puttar", "extract a tar.gz to a buildlet", legacyPutTar)
	registerCommand("rdp", "RDP (Remote Desktop Protocol) to a Windows buildlet", rdp)
	registerCommand("rm", "delete files or directories", legacyRm)
	registerCommand("run", "run a command on a buildlet", legacyRun)
	registerCommand("group", "manage gomote groups (v2 only)", group)
	registerCommand("ssh", "ssh to a buildlet", legacySSH)
}

var (
	serverAddr = flag.String("server", "build.golang.org:443", "Address for GRPC server")
)

func main() {
	// Set up and parse global flags.
	groupName := flag.String("group", os.Getenv("GOMOTE_GROUP"), "name of the gomote group to apply commands to (default is $GOMOTE_GROUP)")
	buildlet.RegisterFlags()
	version := 2
	if vs := os.Getenv("GOMOTE_VERSION"); vs != "" {
		v, err := strconv.Atoi(vs)
		if err == nil {
			version = v
		}
	}
	if version < 1 || version > 2 {
		fmt.Fprintf(os.Stderr, "unsupported version %d", version)
	}
	registerCommands(version)
	flag.Usage = usage
	flag.Parse()
	args := flag.Args()
	if len(args) == 0 {
		usage()
	}

	// Set up globals.
	buildEnv = buildenv.FromFlags()
	if *groupName != "" {
		var err error
		activeGroup, err = loadGroup(*groupName)
		if os.Getenv("GOMOTE_GROUP") != *groupName {
			// Only fail hard since it was specified by the flag.
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failure: %v\n", err)
				usage()
			}
		} else {
			// With a valid group from GOMOTE_GROUP,
			// make it explicit to the user that we're going
			// ahead with it. We don't need this with the flag
			// because it's explicit.
			if err == nil {
				fmt.Fprintf(os.Stderr, "# Using group %q from GOMOTE_GROUP\n", *groupName)
			}
			// Note that an invalid group in GOMOTE_GROUP is OK.
		}
	}

	cmdName := args[0]
	cmd, ok := commands[cmdName]
	if !ok {
		fmt.Fprintf(os.Stderr, "Unknown command %q\n", cmdName)
		usage()
	}
	if err := cmd.run(args[1:]); err != nil {
		logAndExitf("Error running %s: %v\n", cmdName, err)
	}
}

// gomoteServerClient returns a gomote server client which can be used to interact with the gomote GRPC server.
// It will either retrieve a previously created authentication token or attempt to create a new one.
func gomoteServerClient(ctx context.Context) protos.GomoteServiceClient {
	grpcClient, err := iapclient.GRPCClient(ctx, *serverAddr)
	if err != nil {
		logAndExitf("dialing the server=%s failed with: %s", *serverAddr, err)
	}
	return protos.NewGomoteServiceClient(grpcClient)
}

// logAndExitf is equivalent to Printf to Stderr followed by a call to os.Exit(1).
func logAndExitf(format string, v ...interface{}) {
	fmt.Fprintf(os.Stderr, format, v...)
	os.Exit(1)
}

// statusFromError returns the message portion of a GRPC error.
func statusFromError(err error) string {
	return status.Convert(err).Message()
}

func instanceDoesNotExist(err error) bool {
	return status.Code(err) == codes.NotFound
}
<|endoftext|>


<commit_before>/*
Copyright The Helm 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 (
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"

	"helm.sh/helm/cmd/helm/require"
	"helm.sh/helm/pkg/action"
	"helm.sh/helm/pkg/cli/values"
)

const templateDesc = `
Render chart templates locally and display the output.

Any values that would normally be looked up or retrieved in-cluster will be
faked locally. Additionally, none of the server-side testing of chart validity
(e.g. whether an API is supported) is done.
`

func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
	var validate bool
	client := action.NewInstall(cfg)
	valueOpts := &values.Options{}

	cmd := &cobra.Command{
		Use:   "template [NAME] [CHART]",
		Short: fmt.Sprintf("locally render templates"),
		Long:  templateDesc,
		Args:  require.MinimumNArgs(1),
		RunE: func(_ *cobra.Command, args []string) error {
			client.DryRun = true
			client.ReleaseName = "RELEASE-NAME"
			client.Replace = true // Skip the name check
			client.ClientOnly = !validate
			rel, err := runInstall(args, client, valueOpts, out)
			if err != nil {
				return err
			}
			fmt.Fprintln(out, strings.TrimSpace(rel.Manifest))
			return nil
		},
	}

	f := cmd.Flags()
	addInstallFlags(f, client, valueOpts)
	f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
	f.BoolVar(&validate, "validate", false, "establish a connection to Kubernetes for schema validation")

	return cmd
}
<commit_msg>fix(helm3): `helm template` output should include hooks by default<commit_after>/*
Copyright The Helm 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 (
	"fmt"
	"io"
	"strings"

	"github.com/spf13/cobra"

	"helm.sh/helm/cmd/helm/require"
	"helm.sh/helm/pkg/action"
	"helm.sh/helm/pkg/cli/values"
)

const templateDesc = `
Render chart templates locally and display the output.

Any values that would normally be looked up or retrieved in-cluster will be
faked locally. Additionally, none of the server-side testing of chart validity
(e.g. whether an API is supported) is done.
`

func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
	var validate bool
	client := action.NewInstall(cfg)
	valueOpts := &values.Options{}

	cmd := &cobra.Command{
		Use:   "template [NAME] [CHART]",
		Short: fmt.Sprintf("locally render templates"),
		Long:  templateDesc,
		Args:  require.MinimumNArgs(1),
		RunE: func(_ *cobra.Command, args []string) error {
			client.DryRun = true
			client.ReleaseName = "RELEASE-NAME"
			client.Replace = true // Skip the name check
			client.ClientOnly = !validate
			rel, err := runInstall(args, client, valueOpts, out)
			if err != nil {
				return err
			}
			fmt.Fprintln(out, strings.TrimSpace(rel.Manifest))
			if !client.DisableHooks {
				for _, m := range rel.Hooks {
					fmt.Fprintf(out, "---\n# Source: %s\n%s\n", m.Path, m.Manifest)
				}
			}

			return nil
		},
	}

	f := cmd.Flags()
	addInstallFlags(f, client, valueOpts)
	f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
	f.BoolVar(&validate, "validate", false, "establish a connection to Kubernetes for schema validation")

	return cmd
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"launchpad.net/gnuflag"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/machineagent"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver"
	"launchpad.net/juju-core/worker"
	"launchpad.net/juju-core/worker/cleaner"
	"launchpad.net/juju-core/worker/firewaller"
	"launchpad.net/juju-core/worker/machiner"
	"launchpad.net/juju-core/worker/provisioner"
	"launchpad.net/juju-core/worker/resumer"
	"launchpad.net/tomb"
	"path/filepath"
	"time"
)

var retryDelay = 3 * time.Second

// MachineAgent is a cmd.Command responsible for running a machine agent.
type MachineAgent struct {
	cmd.CommandBase
	tomb      tomb.Tomb
	Conf      AgentConf
	MachineId string
	runner    *worker.Runner
}

// Info returns usage information for the command.
func (a *MachineAgent) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "machine",
		Purpose: "run a juju machine agent",
	}
}

func (a *MachineAgent) SetFlags(f *gnuflag.FlagSet) {
	a.Conf.addFlags(f)
	f.StringVar(&a.MachineId, "machine-id", "", "id of the machine to run")
}

// Init initializes the command for running.
func (a *MachineAgent) Init(args []string) error {
	if !state.IsMachineId(a.MachineId) {
		return fmt.Errorf("--machine-id option must be set, and expects a non-negative integer")
	}
	if err := a.Conf.checkArgs(args); err != nil {
		return err
	}
	a.runner = worker.NewRunner(isFatal, moreImportant)
	return nil
}

// Wait waits for the machine agent to finish.
func (a *MachineAgent) Wait() error {
	return a.tomb.Wait()
}

// Stop stops the machine agent.
func (a *MachineAgent) Stop() error {
	a.runner.Kill()
	return a.tomb.Wait()
}

// Run runs a machine agent.
func (a *MachineAgent) Run(_ *cmd.Context) error {
	defer a.tomb.Done()
	log.Infof("machine agent %v start", a.Tag())
	if err := a.Conf.read(a.Tag()); err != nil {
		return err
	}
	if err := EnsureWeHaveLXC(a.Conf.DataDir); err != nil {
		log.Errorf("we were unable to install the lxc package, unable to continue: %v", err)
		return err
	}
	charm.CacheDir = filepath.Join(a.Conf.DataDir, "charmcache")

	// ensureStateWorker ensures that there is a worker that
	// connects to the state that runs within itself all the workers
	// that need a state connection Unless we're bootstrapping, we
	// need to connect to the API server to find out if we need to
	// call this, so we make the APIWorker call it when necessary if
	// the machine requires it.  Note that ensureStateWorker can be
	// called many times - StartWorker does nothing if there is
	// already a worker started with the given name.
	ensureStateWorker := func() {
		a.runner.StartWorker("state", func() (worker.Worker, error) {
			// TODO(rog) go1.1: use method expression
			return a.StateWorker()
		})
	}
	// TODO: Eventually we want to stop creating the StateWorker on nodes
	//       that can function purely with the API connection. However, due
	//       to https://launchpad.net/bugs/1199915 Juju-1.10 never set an
	//       API password for agents, so when upgrading to 1.11 they will
	//       be unable to connect to the API to find out that they need to
	//       start a state worker connection. Since we don't (yet) have any
	//       agents that don't need a state connection, we always start the
	//       state connection.
	//       Once we have agents that actually only need the API
	//       connection, we will have to figure out a different way to
	//       upgrade from 1.10 to that version.
	if true || a.MachineId == "0" {
		// If we're bootstrapping, we don't have an API
		// server to connect to, so start the state worker regardless.

		// TODO(rog) When we have HA, we only want to do this
		// when we really are bootstrapping - once other
		// instances of the API server have been started, we
		// should follow the normal course of things and ignore
		// the fact that this was once the bootstrap machine.
		log.Infof("Starting StateWorker for machine-0")
		ensureStateWorker()
	}
	a.runner.StartWorker("api", func() (worker.Worker, error) {
		// TODO(rog) go1.1: use method expression
		return a.APIWorker(ensureStateWorker)
	})
	err := agentDone(a.runner.Wait())
	a.tomb.Kill(err)
	return err
}

func allFatal(error) bool {
	return true
}

var stateJobs = map[params.MachineJob]bool{
	params.JobHostUnits:     true,
	params.JobManageEnviron: true,
	params.JobManageState:   true,
}

// APIWorker returns a Worker that connects to the API and starts any
// workers that need an API connection.
//
// If a state worker is necessary, APIWorker calls ensureStateWorker.
func (a *MachineAgent) APIWorker(ensureStateWorker func()) (worker.Worker, error) {
	st, entity, err := openAPIState(a.Conf.Conf, a)
	if err != nil {
		return nil, err
	}
	m := entity.(*machineagent.Machine)
	needsStateWorker := false
	for _, job := range m.Jobs() {
		needsStateWorker = needsStateWorker || stateJobs[job]
	}
	if needsStateWorker {
		ensureStateWorker()
	}
	runner := worker.NewRunner(allFatal, moreImportant)
	// No agents currently connect to the API, so just
	// return the runner running nothing.
	return newCloseWorker(runner, st), nil // Note: a worker.Runner is itself a worker.Worker.
}

// StateJobs returns a worker running all the workers that require
// a *state.State connection.
func (a *MachineAgent) StateWorker() (worker.Worker, error) {
	st, entity, err := openState(a.Conf.Conf, a)
	if err != nil {
		return nil, err
	}
	// If this fails, other bits will fail, so we just log the error, and
	// let the other failures actually restart runners
	log.Infof("Calling EnsureAPIPassword for %v", entity)
	if err := EnsureAPIPassword(a.Conf.Conf, entity); err != nil {
		log.Warningf("failed to EnsureAPIPassword: %v", err)
	}
	reportOpenedState(st)
	m := entity.(*state.Machine)
	// TODO(rog) use more discriminating test for errors
	// rather than taking everything down indiscriminately.
	dataDir := a.Conf.DataDir
	runner := worker.NewRunner(allFatal, moreImportant)
	runner.StartWorker("upgrader", func() (worker.Worker, error) {
		// TODO(rog) use id instead of *Machine (or introduce Clone method)
		return NewUpgrader(st, m, dataDir), nil
	})
	runner.StartWorker("machiner", func() (worker.Worker, error) {
		return machiner.NewMachiner(st, m.Id()), nil
	})
	// At this stage, since we don't embed lxc containers, just start an lxc
	// provisioner task for non-lxc containers.  Since we have only LXC
	// containers and normal machines, this effectively means that we only
	// have an LXC provisioner when we have a normally provisioned machine
	// (through the environ-provisioner).  With the upcoming advent of KVM
	// containers, it is likely that we will want an LXC provisioner on a KVM
	// machine, and once we get nested LXC containers, we can remove this
	// check.
	if m.ContainerType() != instance.LXC {
		workerName := fmt.Sprintf("%s-provisioner", provisioner.LXC)
		runner.StartWorker(workerName, func() (worker.Worker, error) {
			return provisioner.NewProvisioner(provisioner.LXC, st, a.MachineId, dataDir), nil
		})
	}
	for _, job := range m.Jobs() {
		switch job {
		case state.JobHostUnits:
			runner.StartWorker("deployer", func() (worker.Worker, error) {
				return newDeployer(st, m.Id(), dataDir), nil
			})
		case state.JobManageEnviron:
			runner.StartWorker("environ-provisioner", func() (worker.Worker, error) {
				return provisioner.NewProvisioner(provisioner.ENVIRON, st, a.MachineId, dataDir), nil
			})
			runner.StartWorker("firewaller", func() (worker.Worker, error) {
				return firewaller.NewFirewaller(st), nil
			})
		case state.JobManageState:
			runner.StartWorker("apiserver", func() (worker.Worker, error) {
				// If the configuration does not have the required information,
				// it is currently not a recoverable error, so we kill the whole
				// agent, potentially enabling human intervention to fix
				// the agent's configuration file. In the future, we may retrieve
				// the state server certificate and key from the state, and
				// this should then change.
				if len(a.Conf.StateServerCert) == 0 || len(a.Conf.StateServerKey) == 0 {
					return nil, &fatalError{"configuration does not have state server cert/key"}
				}
				return apiserver.NewServer(st, fmt.Sprintf(":%d", a.Conf.APIPort), a.Conf.StateServerCert, a.Conf.StateServerKey)
			})
			runner.StartWorker("cleaner", func() (worker.Worker, error) {
				return cleaner.NewCleaner(st), nil
			})
			runner.StartWorker("resumer", func() (worker.Worker, error) {
				// The action of resumer is so subtle that it is not tested,
				// because we can't figure out how to do so without brutalising
				// the transaction log.
				return resumer.NewResumer(st), nil
			})
		default:
			log.Warningf("ignoring unknown job %q", job)
		}
	}
	return newCloseWorker(runner, st), nil
}

func (a *MachineAgent) Entity(st *state.State) (AgentState, error) {
	m, err := st.Machine(a.MachineId)
	if err != nil {
		return nil, err
	}
	// Check the machine nonce as provisioned matches the agent.Conf value.
	if !m.CheckProvisioned(a.Conf.MachineNonce) {
		// The agent is running on a different machine to the one it
		// should be according to state. It must stop immediately.
		log.Errorf("running machine %v agent on inappropriate instance", m)
		return nil, worker.ErrTerminateAgent
	}
	return m, nil
}

func (a *MachineAgent) APIEntity(st *api.State) (AgentAPIState, error) {
	m, err := st.MachineAgent().Machine(a.Tag())
	if err != nil {
		return nil, err
	}
	// TODO(rog) move the CheckProvisioned test into
	// this method when it's implemented in the API
	return m, nil
}

func (a *MachineAgent) Tag() string {
	return state.MachineTag(a.MachineId)
}

// Below pieces are used for testing,to give us access to the *State opened
// by the agent, and allow us to trigger syncs without waiting 5s for them
// to happen automatically.

var stateReporter chan<- *state.State

func reportOpenedState(st *state.State) {
	select {
	case stateReporter <- st:
	default:
	}
}

func sendOpenedStates(dst chan<- *state.State) (undo func()) {
	var original chan<- *state.State
	original, stateReporter = stateReporter, dst
	return func() { stateReporter = original }
}
<commit_msg>Handle the no-api-password differently.<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"fmt"
	"launchpad.net/gnuflag"
	"launchpad.net/juju-core/charm"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/state/api/machineagent"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver"
	"launchpad.net/juju-core/worker"
	"launchpad.net/juju-core/worker/cleaner"
	"launchpad.net/juju-core/worker/firewaller"
	"launchpad.net/juju-core/worker/machiner"
	"launchpad.net/juju-core/worker/provisioner"
	"launchpad.net/juju-core/worker/resumer"
	"launchpad.net/tomb"
	"path/filepath"
	"time"
)

var retryDelay = 3 * time.Second

// MachineAgent is a cmd.Command responsible for running a machine agent.
type MachineAgent struct {
	cmd.CommandBase
	tomb      tomb.Tomb
	Conf      AgentConf
	MachineId string
	runner    *worker.Runner
}

// Info returns usage information for the command.
func (a *MachineAgent) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "machine",
		Purpose: "run a juju machine agent",
	}
}

func (a *MachineAgent) SetFlags(f *gnuflag.FlagSet) {
	a.Conf.addFlags(f)
	f.StringVar(&a.MachineId, "machine-id", "", "id of the machine to run")
}

// Init initializes the command for running.
func (a *MachineAgent) Init(args []string) error {
	if !state.IsMachineId(a.MachineId) {
		return fmt.Errorf("--machine-id option must be set, and expects a non-negative integer")
	}
	if err := a.Conf.checkArgs(args); err != nil {
		return err
	}
	a.runner = worker.NewRunner(isFatal, moreImportant)
	return nil
}

// Wait waits for the machine agent to finish.
func (a *MachineAgent) Wait() error {
	return a.tomb.Wait()
}

// Stop stops the machine agent.
func (a *MachineAgent) Stop() error {
	a.runner.Kill()
	return a.tomb.Wait()
}

// Run runs a machine agent.
func (a *MachineAgent) Run(_ *cmd.Context) error {
	defer a.tomb.Done()
	log.Infof("machine agent %v start", a.Tag())
	if err := a.Conf.read(a.Tag()); err != nil {
		return err
	}
	if err := EnsureWeHaveLXC(a.Conf.DataDir); err != nil {
		log.Errorf("we were unable to install the lxc package, unable to continue: %v", err)
		return err
	}
	charm.CacheDir = filepath.Join(a.Conf.DataDir, "charmcache")

	// ensureStateWorker ensures that there is a worker that
	// connects to the state that runs within itself all the workers
	// that need a state connection Unless we're bootstrapping, we
	// need to connect to the API server to find out if we need to
	// call this, so we make the APIWorker call it when necessary if
	// the machine requires it.  Note that ensureStateWorker can be
	// called many times - StartWorker does nothing if there is
	// already a worker started with the given name.
	ensureStateWorker := func() {
		a.runner.StartWorker("state", func() (worker.Worker, error) {
			// TODO(rog) go1.1: use method expression
			return a.StateWorker()
		})
	}
	if a.MachineId == "0" {
		// If we're bootstrapping, we don't have an API
		// server to connect to, so start the state worker regardless.

		// TODO(rog) When we have HA, we only want to do this
		// when we really are bootstrapping - once other
		// instances of the API server have been started, we
		// should follow the normal course of things and ignore
		// the fact that this was once the bootstrap machine.
		log.Infof("Starting StateWorker for machine-0")
		ensureStateWorker()
	}
	a.runner.StartWorker("api", func() (worker.Worker, error) {
		// TODO(rog) go1.1: use method expression
		return a.APIWorker(ensureStateWorker)
	})
	err := agentDone(a.runner.Wait())
	a.tomb.Kill(err)
	return err
}

func allFatal(error) bool {
	return true
}

var stateJobs = map[params.MachineJob]bool{
	params.JobHostUnits:     true,
	params.JobManageEnviron: true,
	params.JobManageState:   true,
}

// APIWorker returns a Worker that connects to the API and starts any
// workers that need an API connection.
//
// If a state worker is necessary, APIWorker calls ensureStateWorker.
func (a *MachineAgent) APIWorker(ensureStateWorker func()) (worker.Worker, error) {
	st, entity, err := openAPIState(a.Conf.Conf, a)
	if err != nil {
		// There was an error connecting to the API,
		// https://launchpad.net/bugs/1199915 means that we may just
		// not have an API password set. So force a state connection at
		// this point.
		// TODO: Once we can reliably trust that we have API passwords
		//       set, and we no longer need state connections (and
		//       possibly agents will be blocked from connecting
		//       directly to state) we can remove this
		ensureStateWorker()
		return nil, err
	}
	m := entity.(*machineagent.Machine)
	needsStateWorker := false
	for _, job := range m.Jobs() {
		needsStateWorker = needsStateWorker || stateJobs[job]
	}
	if needsStateWorker {
		ensureStateWorker()
	}
	runner := worker.NewRunner(allFatal, moreImportant)
	// No agents currently connect to the API, so just
	// return the runner running nothing.
	return newCloseWorker(runner, st), nil // Note: a worker.Runner is itself a worker.Worker.
}

// StateJobs returns a worker running all the workers that require
// a *state.State connection.
func (a *MachineAgent) StateWorker() (worker.Worker, error) {
	st, entity, err := openState(a.Conf.Conf, a)
	if err != nil {
		return nil, err
	}
	// If this fails, other bits will fail, so we just log the error, and
	// let the other failures actually restart runners
	log.Infof("Calling EnsureAPIPassword for %v", entity)
	if err := EnsureAPIPassword(a.Conf.Conf, entity); err != nil {
		log.Warningf("failed to EnsureAPIPassword: %v", err)
	}
	reportOpenedState(st)
	m := entity.(*state.Machine)
	// TODO(rog) use more discriminating test for errors
	// rather than taking everything down indiscriminately.
	dataDir := a.Conf.DataDir
	runner := worker.NewRunner(allFatal, moreImportant)
	runner.StartWorker("upgrader", func() (worker.Worker, error) {
		// TODO(rog) use id instead of *Machine (or introduce Clone method)
		return NewUpgrader(st, m, dataDir), nil
	})
	runner.StartWorker("machiner", func() (worker.Worker, error) {
		return machiner.NewMachiner(st, m.Id()), nil
	})
	// At this stage, since we don't embed lxc containers, just start an lxc
	// provisioner task for non-lxc containers.  Since we have only LXC
	// containers and normal machines, this effectively means that we only
	// have an LXC provisioner when we have a normally provisioned machine
	// (through the environ-provisioner).  With the upcoming advent of KVM
	// containers, it is likely that we will want an LXC provisioner on a KVM
	// machine, and once we get nested LXC containers, we can remove this
	// check.
	if m.ContainerType() != instance.LXC {
		workerName := fmt.Sprintf("%s-provisioner", provisioner.LXC)
		runner.StartWorker(workerName, func() (worker.Worker, error) {
			return provisioner.NewProvisioner(provisioner.LXC, st, a.MachineId, dataDir), nil
		})
	}
	for _, job := range m.Jobs() {
		switch job {
		case state.JobHostUnits:
			runner.StartWorker("deployer", func() (worker.Worker, error) {
				return newDeployer(st, m.Id(), dataDir), nil
			})
		case state.JobManageEnviron:
			runner.StartWorker("environ-provisioner", func() (worker.Worker, error) {
				return provisioner.NewProvisioner(provisioner.ENVIRON, st, a.MachineId, dataDir), nil
			})
			runner.StartWorker("firewaller", func() (worker.Worker, error) {
				return firewaller.NewFirewaller(st), nil
			})
		case state.JobManageState:
			runner.StartWorker("apiserver", func() (worker.Worker, error) {
				// If the configuration does not have the required information,
				// it is currently not a recoverable error, so we kill the whole
				// agent, potentially enabling human intervention to fix
				// the agent's configuration file. In the future, we may retrieve
				// the state server certificate and key from the state, and
				// this should then change.
				if len(a.Conf.StateServerCert) == 0 || len(a.Conf.StateServerKey) == 0 {
					return nil, &fatalError{"configuration does not have state server cert/key"}
				}
				return apiserver.NewServer(st, fmt.Sprintf(":%d", a.Conf.APIPort), a.Conf.StateServerCert, a.Conf.StateServerKey)
			})
			runner.StartWorker("cleaner", func() (worker.Worker, error) {
				return cleaner.NewCleaner(st), nil
			})
			runner.StartWorker("resumer", func() (worker.Worker, error) {
				// The action of resumer is so subtle that it is not tested,
				// because we can't figure out how to do so without brutalising
				// the transaction log.
				return resumer.NewResumer(st), nil
			})
		default:
			log.Warningf("ignoring unknown job %q", job)
		}
	}
	return newCloseWorker(runner, st), nil
}

func (a *MachineAgent) Entity(st *state.State) (AgentState, error) {
	m, err := st.Machine(a.MachineId)
	if err != nil {
		return nil, err
	}
	// Check the machine nonce as provisioned matches the agent.Conf value.
	if !m.CheckProvisioned(a.Conf.MachineNonce) {
		// The agent is running on a different machine to the one it
		// should be according to state. It must stop immediately.
		log.Errorf("running machine %v agent on inappropriate instance", m)
		return nil, worker.ErrTerminateAgent
	}
	return m, nil
}

func (a *MachineAgent) APIEntity(st *api.State) (AgentAPIState, error) {
	m, err := st.MachineAgent().Machine(a.Tag())
	if err != nil {
		return nil, err
	}
	// TODO(rog) move the CheckProvisioned test into
	// this method when it's implemented in the API
	return m, nil
}

func (a *MachineAgent) Tag() string {
	return state.MachineTag(a.MachineId)
}

// Below pieces are used for testing,to give us access to the *State opened
// by the agent, and allow us to trigger syncs without waiting 5s for them
// to happen automatically.

var stateReporter chan<- *state.State

func reportOpenedState(st *state.State) {
	select {
	case stateReporter <- st:
	default:
	}
}

func sendOpenedStates(dst chan<- *state.State) (undo func()) {
	var original chan<- *state.State
	original, stateReporter = stateReporter, dst
	return func() { stateReporter = original }
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/jalan/kut"
	"reflect"
	"testing"
)

var parseToColNumTests = []struct {
	input  string
	output int
}{
	{"1", 1},
	{"123", 123},
}

var parseToColNumInvalidInputs = []string{
	"",
	"+1",
	"-",
	"-7",
	"0",
	"0xAA",
	"1 2",
	"1.0",
	"B",
	"a",
}

func TestParseToColNum(t *testing.T) {
	for _, pair := range parseToColNumTests {
		output, err := parseToColNum(pair.input)
		if err != nil {
			t.Errorf("parseToColNum(%#v): caused error but should not", pair.input)
		}
		if output != pair.output {
			t.Errorf("parseToColNum(%#v): expected %#v but got %#v", pair.input, pair.output, output)
		}
	}

	for _, input := range parseToColNumInvalidInputs {
		_, err := parseToColNum(input)
		if err == nil {
			t.Errorf("parseToColNum(%#v): should cause error but did not", input)
		}
	}
}

var parseToColRangeTests = []struct {
	input  string
	output kut.ColRange
}{
	{"-10", kut.ColRange{Start: 1, End: 10}},
	{"1", kut.ColRange{Start: 1, End: 1}},
	{"100-", kut.ColRange{Start: 100, End: kut.EOL}},
	{"12-12", kut.ColRange{Start: 12, End: 12}},
	{"2-5", kut.ColRange{Start: 2, End: 5}},
}

var parseToColRangeInvalidInputs = []string{
	"",
	"+1",
	"-",
	"-1-1",
	"0",
	"0xAA",
	"1 2",
	"1.0",
	"10,15",
	"10-9",
	"5-2",
	"B",
	"a",
}

func TestParseToColRange(t *testing.T) {
	for _, pair := range parseToColRangeTests {
		output, err := parseToColRange(pair.input)
		if err != nil {
			t.Errorf("parseToColRange(%#v): caused error but should not", pair.input)
		}
		if output != pair.output {
			t.Errorf("parseToColRange(%#v): expected %#v but got %#v", pair.input, pair.output, output)
		}
	}

	for _, input := range parseToColRangeInvalidInputs {
		_, err := parseToColRange(input)
		if err == nil {
			t.Errorf("parseToColRange(%#v): should cause error but did not", input)
		}
	}
}

var parseToListTests = []struct {
	input  string
	output []kut.ColRange
}{
	{"-1", []kut.ColRange{{Start: 1, End: 1}}},
	{"1", []kut.ColRange{{Start: 1, End: 1}}},
	{"1,1", []kut.ColRange{{Start: 1, End: 1}}},
	{"1,2", []kut.ColRange{{Start: 1, End: 1}, {Start: 2, End: 2}}},
	{"1-", []kut.ColRange{{Start: 1, End: kut.EOL}}},
	{"1-1", []kut.ColRange{{Start: 1, End: 1}}},
	{"2,5-9,12-", []kut.ColRange{{Start: 2, End: 2}, {Start: 5, End: 9}, {Start: 12, End: kut.EOL}}},
	{"5-9", []kut.ColRange{{Start: 5, End: 9}}},
}

var parseToListInvalidInputs = []string{
	"",
	"+1",
	"-",
	"-1-1",
	"0",
	"0xAA",
	"1 2",
	"1.0",
	"10-9",
	"5-2",
	"B",
	"a",
}

func TestParseToList(t *testing.T) {
	for _, pair := range parseToListTests {
		output, err := parseToList(pair.input)
		if err != nil {
			t.Errorf("parseToList(%#v): caused error but should not", pair.input)
		}
		if !reflect.DeepEqual(output, pair.output) {
			t.Errorf("parseToList(%#v): expected %#v but got %#v", pair.input, pair.output, output)
		}
	}

	for _, input := range parseToListInvalidInputs {
		_, err := parseToList(input)
		if err == nil {
			t.Errorf("parseToList(%#v): should cause error but did not", input)
		}
	}
}

var parseArgsTests = []struct {
	input  []string
	output []kut.ColRange
}{
	{[]string{"kut", "5-9"}, []kut.ColRange{{Start: 5, End: 9}}},
}

var parseArgsInvalidInputs = [][]string{
	{"kut", "1-3", "7"},
	{"kut", "file"},
	{"kut"},
}

func TestParseArgs(t *testing.T) {
	for _, pair := range parseArgsTests {
		output, err := parseArgs(pair.input)
		if err != nil {
			t.Errorf("parseArgs(%#v): caused error but should not", pair.input)
		}
		if !reflect.DeepEqual(output, pair.output) {
			t.Errorf("parseArgs(%#v): expected %#v but got %#v", pair.input, pair.output, output)
		}
	}

	for _, input := range parseArgsInvalidInputs {
		_, err := parseArgs(input)
		if err == nil {
			t.Errorf("parseArgs(%#v): should cause error but did not", input)
		}
	}
}
<commit_msg>Convert to table-driven tests<commit_after>package main

import (
	"github.com/jalan/kut"
	"reflect"
	"testing"
)

var parseToColNumTests = []struct {
	input   string
	output  int
	wantErr bool
}{
	{"1", 1, false},
	{"123", 123, false},
	{"", 0, true},
	{"+1", 0, true},
	{"-", 0, true},
	{"-7", 0, true},
	{"0", 0, true},
	{"0xAA", 0, true},
	{"1 2", 0, true},
	{"1.0", 0, true},
	{"B", 0, true},
	{"a", 0, true},
}

func TestParseToColNum(t *testing.T) {
	for _, test := range parseToColNumTests {
		output, err := parseToColNum(test.input)
		if test.wantErr && err == nil {
			t.Errorf("parseToColNum(%#v): expected an error but got nil", test.input)
		}
		if !test.wantErr && err != nil {
			t.Errorf("parseToColNum(%#v): expected nil error but got %#v", test.input, err)
		}
		if output != test.output {
			t.Errorf("parseToColNum(%#v): expected output %#v but got %#v", test.input, test.output, output)
		}
	}
}

var parseToColRangeTests = []struct {
	input   string
	output  kut.ColRange
	wantErr bool
}{
	{"-10", kut.ColRange{Start: 1, End: 10}, false},
	{"1", kut.ColRange{Start: 1, End: 1}, false},
	{"100-", kut.ColRange{Start: 100, End: kut.EOL}, false},
	{"12-12", kut.ColRange{Start: 12, End: 12}, false},
	{"2-5", kut.ColRange{Start: 2, End: 5}, false},
	{"", kut.ColRange{}, true},
	{"+1", kut.ColRange{}, true},
	{"-", kut.ColRange{}, true},
	{"-1-1", kut.ColRange{}, true},
	{"0", kut.ColRange{}, true},
	{"0xAA", kut.ColRange{}, true},
	{"1 2", kut.ColRange{}, true},
	{"1.0", kut.ColRange{}, true},
	{"10,15", kut.ColRange{}, true},
	{"10-9", kut.ColRange{}, true},
	{"5-2", kut.ColRange{}, true},
	{"B", kut.ColRange{}, true},
	{"a", kut.ColRange{}, true},
}

func TestParseToColRange(t *testing.T) {
	for _, test := range parseToColRangeTests {
		output, err := parseToColRange(test.input)
		if test.wantErr && err == nil {
			t.Errorf("parseToColRange(%#v): expected an error but got nil", test.input)
		}
		if !test.wantErr && err != nil {
			t.Errorf("parseToColRange(%#v): expected nil error but got %#v", test.input, err)
		}
		if output != test.output {
			t.Errorf("parseToColRange(%#v): expected output %#v but got %#v", test.input, test.output, output)
		}
	}
}

var parseToListTests = []struct {
	input   string
	output  []kut.ColRange
	wantErr bool
}{
	{"-1", []kut.ColRange{{Start: 1, End: 1}}, false},
	{"1", []kut.ColRange{{Start: 1, End: 1}}, false},
	{"1,1", []kut.ColRange{{Start: 1, End: 1}}, false},
	{"1,2", []kut.ColRange{{Start: 1, End: 1}, {Start: 2, End: 2}}, false},
	{"1-", []kut.ColRange{{Start: 1, End: kut.EOL}}, false},
	{"1-1", []kut.ColRange{{Start: 1, End: 1}}, false},
	{"2,5-9,12-", []kut.ColRange{{Start: 2, End: 2}, {Start: 5, End: 9}, {Start: 12, End: kut.EOL}}, false},
	{"5-9", []kut.ColRange{{Start: 5, End: 9}}, false},
	{"", nil, true},
	{"+1", nil, true},
	{"-", nil, true},
	{"-1-1", nil, true},
	{"0", nil, true},
	{"0xAA", nil, true},
	{"1 2", nil, true},
	{"1.0", nil, true},
	{"10-9", nil, true},
	{"5-2", nil, true},
	{"B", nil, true},
	{"a", nil, true},
}

func TestParseToList(t *testing.T) {
	for _, test := range parseToListTests {
		output, err := parseToList(test.input)
		if test.wantErr && err == nil {
			t.Errorf("parseToList(%#v): expected an error but got nil", test.input)
		}
		if !test.wantErr && err != nil {
			t.Errorf("parseToList(%#v): expected nil error but got %#v", test.input, err)
		}
		if !reflect.DeepEqual(output, test.output) {
			t.Errorf("parseToList(%#v): expected output %#v but got %#v", test.input, test.output, output)
		}
	}
}

var parseArgsTests = []struct {
	input   []string
	output  []kut.ColRange
	wantErr bool
}{
	{[]string{"kut", "5-9"}, []kut.ColRange{{Start: 5, End: 9}}, false},
	{[]string{"kut", "1-3", "7"}, nil, true},
	{[]string{"kut", "file"}, nil, true},
	{[]string{"kut"}, nil, true},
}

func TestParseArgs(t *testing.T) {
	for _, test := range parseArgsTests {
		output, err := parseArgs(test.input)
		if test.wantErr && err == nil {
			t.Errorf("parseArgs(%#v): expected an error but got nil", test.input)
		}
		if !test.wantErr && err != nil {
			t.Errorf("parseArgs(%#v): expected nil error but got %#v", test.input, err)
		}
		if !reflect.DeepEqual(output, test.output) {
			t.Errorf("parseArgs(%#v): expected output %#v but got %#v", test.input, test.output, output)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The etcd-operator 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 (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"time"

	"github.com/coreos/etcd-operator/pkg/analytics"
	"github.com/coreos/etcd-operator/pkg/backup/s3/s3config"
	"github.com/coreos/etcd-operator/pkg/chaos"
	"github.com/coreos/etcd-operator/pkg/client"
	"github.com/coreos/etcd-operator/pkg/controller"
	"github.com/coreos/etcd-operator/pkg/debug"
	"github.com/coreos/etcd-operator/pkg/garbagecollection"
	"github.com/coreos/etcd-operator/pkg/util/constants"
	"github.com/coreos/etcd-operator/pkg/util/k8sutil"
	"github.com/coreos/etcd-operator/pkg/util/probe"
	"github.com/coreos/etcd-operator/pkg/util/retryutil"
	"github.com/coreos/etcd-operator/version"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/Sirupsen/logrus"
	"golang.org/x/time/rate"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
)

var (
	analyticsEnabled bool
	pvProvisioner    string
	namespace        string
	name             string
	awsSecret        string
	awsConfig        string
	s3Bucket         string
	listenAddr       string
	gcInterval       time.Duration

	chaosLevel int

	printVersion bool
)

func init() {
	flag.BoolVar(&analyticsEnabled, "analytics", true, "Send analytical event (Cluster Created/Deleted etc.) to Google Analytics")
	flag.StringVar(&debug.DebugFilePath, "debug-logfile-path", "", "only for a self hosted cluster, the path where the debug logfile will be written, recommended to be under: /var/tmp/etcd-operator/debug/ to avoid any issue with lack of write permissions")

	flag.StringVar(&pvProvisioner, "pv-provisioner", constants.PVProvisionerGCEPD, "persistent volume provisioner type")
	flag.StringVar(&awsSecret, "backup-aws-secret", "",
		"DEPRECATED - The name of the kube secret object that stores the AWS credential file. The file name must be 'credentials'.")
	flag.StringVar(&awsConfig, "backup-aws-config", "",
		"DEPRECATED - The name of the kube configmap object that stores the AWS config file. The file name must be 'config'.")
	flag.StringVar(&s3Bucket, "backup-s3-bucket", "", "DEPRECATED - The name of the AWS S3 bucket to store backups in.")
	flag.StringVar(&listenAddr, "listen-addr", "0.0.0.0:8080", "The address on which the HTTP server will listen to")
	// chaos level will be removed once we have a formal tool to inject failures.
	flag.IntVar(&chaosLevel, "chaos-level", -1, "DO NOT USE IN PRODUCTION - level of chaos injected into the etcd clusters created by the operator.")
	flag.BoolVar(&printVersion, "version", false, "Show version and quit")
	flag.DurationVar(&gcInterval, "gc-interval", 10*time.Minute, "GC interval")
	flag.Parse()

	// TODO: remove this and use CR client
	restCfg, err := k8sutil.InClusterConfig()
	if err != nil {
		panic(err)
	}
	controller.MasterHost = restCfg.Host
	restcli, _, err := client.New(restCfg)
	if err != nil {
		panic(err)
	}
	controller.KubeHttpCli = restcli.Client
}

func main() {
	namespace = os.Getenv("MY_POD_NAMESPACE")
	if len(namespace) == 0 {
		logrus.Fatalf("must set env MY_POD_NAMESPACE")
	}
	name = os.Getenv("MY_POD_NAME")
	if len(name) == 0 {
		logrus.Fatalf("must set env MY_POD_NAME")
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c)
	go func() {
		logrus.Infof("received signal: %v", <-c)
		os.Exit(1)
	}()

	if printVersion {
		fmt.Println("etcd-operator Version:", version.Version)
		fmt.Println("Git SHA:", version.GitSHA)
		fmt.Println("Go Version:", runtime.Version())
		fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
		os.Exit(0)
	}

	logrus.Infof("etcd-operator Version: %v", version.Version)
	logrus.Infof("Git SHA: %s", version.GitSHA)
	logrus.Infof("Go Version: %s", runtime.Version())
	logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)

	if analyticsEnabled {
		analytics.Enable()
	}

	analytics.OperatorStarted()

	id, err := os.Hostname()
	if err != nil {
		logrus.Fatalf("failed to get hostname: %v", err)
	}

	kubecli := k8sutil.MustNewKubeClient()

	http.HandleFunc(probe.HTTPReadyzEndpoint, probe.ReadyzHandler)
	http.Handle("/metrics", prometheus.Handler())
	go http.ListenAndServe(listenAddr, nil)

	rl, err := resourcelock.New(resourcelock.EndpointsResourceLock,
		namespace,
		"etcd-operator",
		kubecli.(*kubernetes.Clientset),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: createRecorder(kubecli, name, namespace),
		})
	if err != nil {
		logrus.Fatalf("error creating lock: %v", err)
	}

	leaderelection.RunOrDie(leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: 15 * time.Second,
		RenewDeadline: 10 * time.Second,
		RetryPeriod:   2 * time.Second,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: run,
			OnStoppedLeading: func() {
				logrus.Fatalf("leader election lost")
			},
		},
	})

	panic("unreachable")
}

func run(stop <-chan struct{}) {
	cfg := newControllerConfig()
	if err := cfg.Validate(); err != nil {
		logrus.Fatalf("invalid operator config: %v", err)
	}

	go periodicFullGC(cfg.KubeCli, cfg.Namespace, gcInterval)

	startChaos(context.Background(), cfg.KubeCli, cfg.Namespace, chaosLevel)

	c := controller.New(cfg)
	err := c.Start()
	logrus.Fatalf("controller Start() failed: %v", err)
}

func newControllerConfig() controller.Config {
	kubecli := k8sutil.MustNewKubeClient()

	serviceAccount, err := getMyPodServiceAccount(kubecli)
	if err != nil {
		logrus.Fatalf("fail to get my pod's service account: %v", err)
	}

	// TODO: remove this when deleting aws config flags.
	anySet := len(awsConfig) != 0 || len(s3Bucket) != 0 || len(awsSecret) != 0
	if anySet {
		logrus.Warn("Saving backups to S3 via operator flags is deprecated; use Cluster level configuration instead.")
	}

	cfg := controller.Config{
		Namespace:      namespace,
		ServiceAccount: serviceAccount,
		PVProvisioner:  pvProvisioner,
		S3Context: s3config.S3Context{
			AWSSecret: awsSecret,
			AWSConfig: awsConfig,
			S3Bucket:  s3Bucket,
		},
		KubeCli:    kubecli,
		KubeExtCli: k8sutil.MustNewKubeExtClient(),
		EtcdCRCli:  client.MustNewCRInCluster(),
	}

	return cfg
}

func getMyPodServiceAccount(kubecli kubernetes.Interface) (string, error) {
	var sa string
	err := retryutil.Retry(5*time.Second, 100, func() (bool, error) {
		pod, err := kubecli.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
		if err != nil {
			logrus.Errorf("fail to get operator pod (%s): %v", name, err)
			return false, nil
		}
		sa = pod.Spec.ServiceAccountName
		return true, nil
	})
	return sa, err
}

func periodicFullGC(kubecli kubernetes.Interface, ns string, d time.Duration) {
	gc := garbagecollection.New(kubecli, ns)
	timer := time.NewTicker(d)
	defer timer.Stop()
	for {
		<-timer.C
		err := gc.FullyCollect()
		if err != nil {
			logrus.Warningf("failed to cleanup resources: %v", err)
		}
	}
}

func startChaos(ctx context.Context, kubecli kubernetes.Interface, ns string, chaosLevel int) {
	m := chaos.NewMonkeys(kubecli)
	ls := labels.SelectorFromSet(map[string]string{"app": "etcd"})

	switch chaosLevel {
	case 1:
		logrus.Info("chaos level = 1: randomly kill one etcd pod every 30 seconds at 50%")
		c := &chaos.CrashConfig{
			Namespace: ns,
			Selector:  ls,

			KillRate:        rate.Every(30 * time.Second),
			KillProbability: 0.5,
			KillMax:         1,
		}
		go func() {
			time.Sleep(60 * time.Second) // don't start until quorum up
			m.CrushPods(ctx, c)
		}()

	case 2:
		logrus.Info("chaos level = 2: randomly kill at most five etcd pods every 30 seconds at 50%")
		c := &chaos.CrashConfig{
			Namespace: ns,
			Selector:  ls,

			KillRate:        rate.Every(30 * time.Second),
			KillProbability: 0.5,
			KillMax:         5,
		}

		go m.CrushPods(ctx, c)

	default:
	}
}

func createRecorder(kubecli kubernetes.Interface, name, namespace string) record.EventRecorder {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(logrus.Infof)
	eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubecli.Core().RESTClient()).Events(namespace)})
	return eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: name})
}
<commit_msg>operator: remove s3 flags from operator<commit_after>// Copyright 2016 The etcd-operator 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 (
	"context"
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"runtime"
	"time"

	"github.com/coreos/etcd-operator/pkg/analytics"
	"github.com/coreos/etcd-operator/pkg/chaos"
	"github.com/coreos/etcd-operator/pkg/client"
	"github.com/coreos/etcd-operator/pkg/controller"
	"github.com/coreos/etcd-operator/pkg/debug"
	"github.com/coreos/etcd-operator/pkg/garbagecollection"
	"github.com/coreos/etcd-operator/pkg/util/constants"
	"github.com/coreos/etcd-operator/pkg/util/k8sutil"
	"github.com/coreos/etcd-operator/pkg/util/probe"
	"github.com/coreos/etcd-operator/pkg/util/retryutil"
	"github.com/coreos/etcd-operator/version"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/Sirupsen/logrus"
	"golang.org/x/time/rate"
	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/kubernetes/scheme"
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
	"k8s.io/client-go/tools/leaderelection"
	"k8s.io/client-go/tools/leaderelection/resourcelock"
	"k8s.io/client-go/tools/record"
)

var (
	analyticsEnabled bool
	pvProvisioner    string
	namespace        string
	name             string
	listenAddr       string
	gcInterval       time.Duration

	chaosLevel int

	printVersion bool
)

func init() {
	flag.BoolVar(&analyticsEnabled, "analytics", true, "Send analytical event (Cluster Created/Deleted etc.) to Google Analytics")
	flag.StringVar(&debug.DebugFilePath, "debug-logfile-path", "", "only for a self hosted cluster, the path where the debug logfile will be written, recommended to be under: /var/tmp/etcd-operator/debug/ to avoid any issue with lack of write permissions")
	flag.StringVar(&pvProvisioner, "pv-provisioner", constants.PVProvisionerGCEPD, "persistent volume provisioner type")
	flag.StringVar(&listenAddr, "listen-addr", "0.0.0.0:8080", "The address on which the HTTP server will listen to")
	// chaos level will be removed once we have a formal tool to inject failures.
	flag.IntVar(&chaosLevel, "chaos-level", -1, "DO NOT USE IN PRODUCTION - level of chaos injected into the etcd clusters created by the operator.")
	flag.BoolVar(&printVersion, "version", false, "Show version and quit")
	flag.DurationVar(&gcInterval, "gc-interval", 10*time.Minute, "GC interval")
	flag.Parse()

	// TODO: remove this and use CR client
	restCfg, err := k8sutil.InClusterConfig()
	if err != nil {
		panic(err)
	}
	controller.MasterHost = restCfg.Host
	restcli, _, err := client.New(restCfg)
	if err != nil {
		panic(err)
	}
	controller.KubeHttpCli = restcli.Client
}

func main() {
	namespace = os.Getenv("MY_POD_NAMESPACE")
	if len(namespace) == 0 {
		logrus.Fatalf("must set env MY_POD_NAMESPACE")
	}
	name = os.Getenv("MY_POD_NAME")
	if len(name) == 0 {
		logrus.Fatalf("must set env MY_POD_NAME")
	}

	c := make(chan os.Signal, 1)
	signal.Notify(c)
	go func() {
		logrus.Infof("received signal: %v", <-c)
		os.Exit(1)
	}()

	if printVersion {
		fmt.Println("etcd-operator Version:", version.Version)
		fmt.Println("Git SHA:", version.GitSHA)
		fmt.Println("Go Version:", runtime.Version())
		fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
		os.Exit(0)
	}

	logrus.Infof("etcd-operator Version: %v", version.Version)
	logrus.Infof("Git SHA: %s", version.GitSHA)
	logrus.Infof("Go Version: %s", runtime.Version())
	logrus.Infof("Go OS/Arch: %s/%s", runtime.GOOS, runtime.GOARCH)

	if analyticsEnabled {
		analytics.Enable()
	}

	analytics.OperatorStarted()

	id, err := os.Hostname()
	if err != nil {
		logrus.Fatalf("failed to get hostname: %v", err)
	}

	kubecli := k8sutil.MustNewKubeClient()

	http.HandleFunc(probe.HTTPReadyzEndpoint, probe.ReadyzHandler)
	http.Handle("/metrics", prometheus.Handler())
	go http.ListenAndServe(listenAddr, nil)

	rl, err := resourcelock.New(resourcelock.EndpointsResourceLock,
		namespace,
		"etcd-operator",
		kubecli.(*kubernetes.Clientset),
		resourcelock.ResourceLockConfig{
			Identity:      id,
			EventRecorder: createRecorder(kubecli, name, namespace),
		})
	if err != nil {
		logrus.Fatalf("error creating lock: %v", err)
	}

	leaderelection.RunOrDie(leaderelection.LeaderElectionConfig{
		Lock:          rl,
		LeaseDuration: 15 * time.Second,
		RenewDeadline: 10 * time.Second,
		RetryPeriod:   2 * time.Second,
		Callbacks: leaderelection.LeaderCallbacks{
			OnStartedLeading: run,
			OnStoppedLeading: func() {
				logrus.Fatalf("leader election lost")
			},
		},
	})

	panic("unreachable")
}

func run(stop <-chan struct{}) {
	cfg := newControllerConfig()
	if err := cfg.Validate(); err != nil {
		logrus.Fatalf("invalid operator config: %v", err)
	}

	go periodicFullGC(cfg.KubeCli, cfg.Namespace, gcInterval)

	startChaos(context.Background(), cfg.KubeCli, cfg.Namespace, chaosLevel)

	c := controller.New(cfg)
	err := c.Start()
	logrus.Fatalf("controller Start() failed: %v", err)
}

func newControllerConfig() controller.Config {
	kubecli := k8sutil.MustNewKubeClient()

	serviceAccount, err := getMyPodServiceAccount(kubecli)
	if err != nil {
		logrus.Fatalf("fail to get my pod's service account: %v", err)
	}

	cfg := controller.Config{
		Namespace:      namespace,
		ServiceAccount: serviceAccount,
		PVProvisioner:  pvProvisioner,
		KubeCli:        kubecli,
		KubeExtCli:     k8sutil.MustNewKubeExtClient(),
		EtcdCRCli:      client.MustNewCRInCluster(),
	}

	return cfg
}

func getMyPodServiceAccount(kubecli kubernetes.Interface) (string, error) {
	var sa string
	err := retryutil.Retry(5*time.Second, 100, func() (bool, error) {
		pod, err := kubecli.CoreV1().Pods(namespace).Get(name, metav1.GetOptions{})
		if err != nil {
			logrus.Errorf("fail to get operator pod (%s): %v", name, err)
			return false, nil
		}
		sa = pod.Spec.ServiceAccountName
		return true, nil
	})
	return sa, err
}

func periodicFullGC(kubecli kubernetes.Interface, ns string, d time.Duration) {
	gc := garbagecollection.New(kubecli, ns)
	timer := time.NewTicker(d)
	defer timer.Stop()
	for {
		<-timer.C
		err := gc.FullyCollect()
		if err != nil {
			logrus.Warningf("failed to cleanup resources: %v", err)
		}
	}
}

func startChaos(ctx context.Context, kubecli kubernetes.Interface, ns string, chaosLevel int) {
	m := chaos.NewMonkeys(kubecli)
	ls := labels.SelectorFromSet(map[string]string{"app": "etcd"})

	switch chaosLevel {
	case 1:
		logrus.Info("chaos level = 1: randomly kill one etcd pod every 30 seconds at 50%")
		c := &chaos.CrashConfig{
			Namespace: ns,
			Selector:  ls,

			KillRate:        rate.Every(30 * time.Second),
			KillProbability: 0.5,
			KillMax:         1,
		}
		go func() {
			time.Sleep(60 * time.Second) // don't start until quorum up
			m.CrushPods(ctx, c)
		}()

	case 2:
		logrus.Info("chaos level = 2: randomly kill at most five etcd pods every 30 seconds at 50%")
		c := &chaos.CrashConfig{
			Namespace: ns,
			Selector:  ls,

			KillRate:        rate.Every(30 * time.Second),
			KillProbability: 0.5,
			KillMax:         5,
		}

		go m.CrushPods(ctx, c)

	default:
	}
}

func createRecorder(kubecli kubernetes.Interface, name, namespace string) record.EventRecorder {
	eventBroadcaster := record.NewBroadcaster()
	eventBroadcaster.StartLogging(logrus.Infof)
	eventBroadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: v1core.New(kubecli.Core().RESTClient()).Events(namespace)})
	return eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: name})
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// 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 org

import (
	"fmt"

	"github.com/spf13/cobra"
	"github.com/srinandan/apigeecli/apiclient"
	"github.com/srinandan/apigeecli/client/orgs"
)

//Cmd to get org details
var CreateCmd = &cobra.Command{
	Use:   "create",
	Short: "Create a new Apigee Org",
	Long:  "Create a new Apigee Org; Your GCP project must be whitelist for this operation",
	Args: func(cmd *cobra.Command, args []string) (err error) {
		if runtimeType != "HYBRID" && runtimeType != "CLOUD" {
			return fmt.Errorf("runtime type must be CLOUD or HYBRID")
		}
		if runtimeType == "CLOUD" {
			if network == "" {
				return fmt.Errorf("authorized network must be supplied")
			}
			if databaseKey == "" {
				return fmt.Errorf("runtime database encryption key must be supplied")
			}
		}
		apiclient.SetProjectID(projectID)
		return apiclient.SetApigeeOrg(projectID)
	},
	RunE: func(cmd *cobra.Command, args []string) (err error) {
		_, err = orgs.Create(region, network, runtimeType, databaseKey)
		return
	},
}

var region, projectID, network, runtimeType, databaseKey string

func init() {

	CreateCmd.Flags().StringVarP(&region, "reg", "r",
		"", "Analytics region name")
	CreateCmd.Flags().StringVarP(&projectID, "prj", "p",
		"", "GCP Project ID")
	CreateCmd.Flags().StringVarP(&network, "net", "n",
		"default", "Authorized network")
	CreateCmd.Flags().StringVarP(&databaseKey, "key", "k",
		"", "Runtime Database Encryption Key")
	CreateCmd.Flags().StringVarP(&runtimeType, "runtime-type", "",
		"HYBRID", "Runtime type: CLOUD or HYBRID")

	_ = CreateCmd.MarkFlagRequired("prj")
	_ = CreateCmd.MarkFlagRequired("reg")
	_ = CreateCmd.MarkFlagRequired("runtime-type")
}
<commit_msg>improve help for create org<commit_after>// Copyright 2020 Google LLC
//
// 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 org

import (
	"fmt"

	"github.com/spf13/cobra"
	"github.com/srinandan/apigeecli/apiclient"
	"github.com/srinandan/apigeecli/client/orgs"
)

//Cmd to get org details
var CreateCmd = &cobra.Command{
	Use:   "create",
	Short: "Create a new Apigee Org",
	Long:  "Create a new Apigee Org; Your GCP project must be whitelist for this operation",
	Args: func(cmd *cobra.Command, args []string) (err error) {
		if runtimeType != "HYBRID" && runtimeType != "CLOUD" {
			return fmt.Errorf("runtime type must be CLOUD or HYBRID")
		}
		if runtimeType == "CLOUD" {
			if network == "" {
				return fmt.Errorf("authorized network must be supplied")
			}
			if databaseKey == "" {
				return fmt.Errorf("runtime database encryption key must be supplied")
			}
		}
		apiclient.SetProjectID(projectID)
		return apiclient.SetApigeeOrg(projectID)
	},
	RunE: func(cmd *cobra.Command, args []string) (err error) {
		_, err = orgs.Create(region, network, runtimeType, databaseKey)
		return
	},
}

var region, projectID, network, runtimeType, databaseKey string

func init() {

	CreateCmd.Flags().StringVarP(&region, "reg", "r",
		"", "Analytics region name")
	CreateCmd.Flags().StringVarP(&projectID, "prj", "p",
		"", "GCP Project ID")
	CreateCmd.Flags().StringVarP(&network, "net", "n",
		"default", "Authorized network; if using a shared VPC format is projects/{host-project-id}/{location}/networks/{network-name}")
	CreateCmd.Flags().StringVarP(&databaseKey, "key", "k",
		"", "Runtime Database Encryption Key")
	CreateCmd.Flags().StringVarP(&runtimeType, "runtime-type", "",
		"HYBRID", "Runtime type: CLOUD or HYBRID")

	_ = CreateCmd.MarkFlagRequired("prj")
	_ = CreateCmd.MarkFlagRequired("reg")
	_ = CreateCmd.MarkFlagRequired("runtime-type")
}
<|endoftext|>