<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/datawire/ambassador/pkg/k8s"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

func aesInstallCmd() *cobra.Command {
	res := &cobra.Command{
		Use:   "install",
		Short: "Install the Ambassador Edge Stack in your cluster",
		Args:  cobra.ExactArgs(0),
		RunE:  aesInstall,
	}
	_ = res.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = res.Flags().StringP(
		"namespace", "n", "",
		"The Kubernetes namespace to use. Defaults to kubectl's default for the context.",
	)

	return res
}

func aesInstall(cmd *cobra.Command, args []string) error {
	metrics := NewMetrics()
	_ = metrics.Report("install")

	// Display version information
	fmt.Printf("-> Installing the Ambassador Edge Stack %s\n", Version)

	// Attempt to talk to the specified cluster
	context, _ := cmd.Flags().GetString("context")
	namespace, _ := cmd.Flags().GetString("namespace")
	kubeinfo := k8s.NewKubeInfo("", context, namespace)
	i := &Installer{
		kubeinfo,
	}
	if err := i.ShowKubectl("cluster-info", "cluster-info"); err != nil {
		return err
	}

	if err := i.ShowKubectl("install CRDs", "apply", "-f", "https://www.getambassador.io/yaml/aes-crds.yaml"); err != nil {
		return err
	}

	if err := i.ShowKubectl("wait for CRDs", "wait", "--for", "condition=established", "--timeout=90s", "crd", "-lproduct=aes"); err != nil {
		return err
	}

	if err := i.ShowKubectl("install AES", "apply", "-f", "https://www.getambassador.io/yaml/aes.yaml"); err != nil {
		return err
	}

	if err := i.ShowKubectl("wait for AES", "-n", "ambassador", "wait", "--for", "condition=available", "--timeout=90s", "deploy", "-lproduct=aes"); err != nil {
		return err
	}

	_ = metrics.Report("deploy") // TODO: Send cluster type and Helm version

	ipAddress := ""
	for {
		var err error
		ipAddress, err = i.CaptureKubectl("get IP address", "get", "-n", "ambassador", "service", "ambassador", "-o", `go-template={{range .status.loadBalancer.ingress}}{{print .ip "\n"}}{{end}}`)
		if err != nil {
			return err
		}
		ipAddress = strings.TrimSpace(ipAddress)
		if ipAddress != "" {
			break
		}
		time.Sleep(250 * time.Millisecond) // FIXME: Time out at some point...
	}

	fmt.Println("Your IP address is", ipAddress)

	// Send a request to acquire a DNS name for this cluster's IP address
	regURL := "https://metriton.datawire.io/beta/register-domain"
	emailAddress := "ark3+eci@datawire.io"
	buf := new(bytes.Buffer)
	_ = json.NewEncoder(buf).Encode(registration{emailAddress, ipAddress})
	resp, err := http.Post(regURL, "application/json", buf)
	if err != nil {
		return errors.Wrap(err, "acquire DNS name (post)")
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return errors.Wrap(err, "acquire DNS name (read body)")
	}

	if resp.StatusCode == 200 {
		hostname := string(content)
		fmt.Println("-> Acquiring DNS name", hostname)

		// Wait for DNS to propagate. This tries to avoid waiting for a ten
		// minute error backoff if the ACME registration races ahead of the DNS
		// name appearing for LetsEncrypt.
		for {
			conn, err := net.Dial("tcp", hostname+":443")
			if err == nil {
				conn.Close()
				break
			}
			// fmt.Printf("Waiting for DNS: %#v\n", err)
			time.Sleep(500 * time.Millisecond)
		}
		fmt.Println("-> Automatically configuring TLS")
		fmt.Println("Please enter an email address. We'll use this email address to notify you prior to domain and certification expiration [None]:", emailAddress)
		fmt.Println("FIXME: let the user enter an address")
		// Create a Host resource
		hostResource := fmt.Sprintf(hostManifest, hostname, namespace, hostname, emailAddress)
		kargs, err := i.kubeinfo.GetKubectlArray("apply", "-f", "-")
		if err != nil {
			return errors.Wrapf(err, "cluster access for install AES")
		}
		fmt.Println("\n$ kubectl apply -f - < [Host Resource]")
		cmd := exec.Command("kubectl", kargs...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = strings.NewReader(hostResource)
		if err := cmd.Run(); err != nil {
			return errors.Wrap(err, "install AES")
		}

		fmt.Println("\n-> Obtaining a TLS certificate from Let's Encrypt")

		for {
			state, err := i.CaptureKubectl("get Host state", "get", "host", hostname, "-o", "go-template={{.status.state}}")
			if err != nil {
				return err
			}
			if state == "Ready" {
				break
			}
			time.Sleep(500 * time.Millisecond) // FIXME: Time out at some point...
			// FIXME: Do something smart for state == "Error"
		}

		_ = metrics.Report("cert_provisioned")
		fmt.Println("-> TLS configured successfully")
		if err := i.ShowKubectl("show Host", "get", "host", hostname); err != nil {
			return err
		}

		// TODO: Open browser (do edgectl login)

	} else {
		fmt.Println("-> Failed to create a DNS name", content)
		//   if reachable from host (e.g., k3s, special case for Minikube?)
		//     open a browser window to http...
		//   else
		//     suggest port-forward to reach policy console?
	}

	_ = metrics.Report("aes_health_good") // or aes_health_bad TODO: Send cluster's install_id and AES version

	return nil
}

type Installer struct {
	kubeinfo *k8s.KubeInfo
}

// Kubernetes Cluster

func (i *Installer) ShowKubectl(name string, args ...string) error {
	kargs, err := i.kubeinfo.GetKubectlArray(args...)
	if err != nil {
		return errors.Wrapf(err, "cluster access for %s", name)
	}
	fmt.Printf("\n$ kubectl %s\n", strings.Join(kargs, " "))
	cmd := exec.Command("kubectl", kargs...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return errors.Wrap(err, name)
	}
	return nil
}

func (i *Installer) CaptureKubectl(name string, args ...string) (res string, err error) {
	res = ""
	kargs, err := i.kubeinfo.GetKubectlArray(args...)
	if err != nil {
		err = errors.Wrapf(err, "cluster access for %s", name)
		return
	}
	fmt.Printf("\n$ kubectl %s\n", strings.Join(kargs, " "))
	cmd := exec.Command("kubectl", kargs...)
	cmd.Stderr = nil
	resAsBytes, err := cmd.Output()
	if err != nil {
		if ee, ok := err.(*exec.ExitError); ok {
			fmt.Println(ee.Stderr)
		}
		err = errors.Wrap(err, name)
	}
	res = string(resAsBytes)
	return
}

// DNS Registration

type registration struct {
	Email string
	Ip    string
}

// Metrics

type Metrics struct {
	InstallID string
}

func NewMetrics() *Metrics {
	// TODO: Read or create an installation ID
	return nil
}

func (m *Metrics) Report(eventName string) error {
	fmt.Println("-> [Metrics]", eventName)
	return nil
}

const hostManifest = `
apiVersion: getambassador.io/v2
kind: Host
metadata:
  name: %s
  namespace: %s
spec:
  hostname: %s
  acmeProvider:
    email: %s
`
<commit_msg>Call login from install<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/datawire/ambassador/pkg/k8s"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

func aesInstallCmd() *cobra.Command {
	res := &cobra.Command{
		Use:   "install",
		Short: "Install the Ambassador Edge Stack in your cluster",
		Args:  cobra.ExactArgs(0),
		RunE:  aesInstall,
	}
	_ = res.Flags().StringP(
		"context", "c", "",
		"The Kubernetes context to use. Defaults to the current kubectl context.",
	)
	_ = res.Flags().StringP(
		"namespace", "n", "",
		"The Kubernetes namespace to use. Defaults to kubectl's default for the context.",
	)

	return res
}

func aesInstall(cmd *cobra.Command, args []string) error {
	metrics := NewMetrics()
	_ = metrics.Report("install")

	// Display version information
	fmt.Printf("-> Installing the Ambassador Edge Stack %s\n", Version)

	// Attempt to talk to the specified cluster
	context, _ := cmd.Flags().GetString("context")
	namespace, _ := cmd.Flags().GetString("namespace")
	kubeinfo := k8s.NewKubeInfo("", context, namespace)
	i := &Installer{
		kubeinfo,
	}
	if err := i.ShowKubectl("cluster-info", "cluster-info"); err != nil {
		return err
	}

	if err := i.ShowKubectl("install CRDs", "apply", "-f", "https://www.getambassador.io/yaml/aes-crds.yaml"); err != nil {
		return err
	}

	if err := i.ShowKubectl("wait for CRDs", "wait", "--for", "condition=established", "--timeout=90s", "crd", "-lproduct=aes"); err != nil {
		return err
	}

	if err := i.ShowKubectl("install AES", "apply", "-f", "https://www.getambassador.io/yaml/aes.yaml"); err != nil {
		return err
	}

	if err := i.ShowKubectl("wait for AES", "-n", "ambassador", "wait", "--for", "condition=available", "--timeout=90s", "deploy", "-lproduct=aes"); err != nil {
		return err
	}

	_ = metrics.Report("deploy") // TODO: Send cluster type and Helm version

	ipAddress := ""
	for {
		var err error
		ipAddress, err = i.CaptureKubectl("get IP address", "get", "-n", "ambassador", "service", "ambassador", "-o", `go-template={{range .status.loadBalancer.ingress}}{{print .ip "\n"}}{{end}}`)
		if err != nil {
			return err
		}
		ipAddress = strings.TrimSpace(ipAddress)
		if ipAddress != "" {
			break
		}
		time.Sleep(250 * time.Millisecond) // FIXME: Time out at some point...
	}

	fmt.Println("Your IP address is", ipAddress)

	// Send a request to acquire a DNS name for this cluster's IP address
	regURL := "https://metriton.datawire.io/beta/register-domain"
	emailAddress := "ark3+eci@datawire.io"
	buf := new(bytes.Buffer)
	_ = json.NewEncoder(buf).Encode(registration{emailAddress, ipAddress})
	resp, err := http.Post(regURL, "application/json", buf)
	if err != nil {
		return errors.Wrap(err, "acquire DNS name (post)")
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	if err != nil {
		return errors.Wrap(err, "acquire DNS name (read body)")
	}

	if resp.StatusCode == 200 {
		hostname := string(content)
		fmt.Println("-> Acquiring DNS name", hostname)

		// Wait for DNS to propagate. This tries to avoid waiting for a ten
		// minute error backoff if the ACME registration races ahead of the DNS
		// name appearing for LetsEncrypt.
		for {
			conn, err := net.Dial("tcp", hostname+":443")
			if err == nil {
				conn.Close()
				break
			}
			// fmt.Printf("Waiting for DNS: %#v\n", err)
			time.Sleep(500 * time.Millisecond)
		}
		fmt.Println("-> Automatically configuring TLS")
		fmt.Println("Please enter an email address. We'll use this email address to notify you prior to domain and certification expiration [None]:", emailAddress)
		fmt.Println("FIXME: let the user enter an address")
		// Create a Host resource
		hostResource := fmt.Sprintf(hostManifest, hostname, namespace, hostname, emailAddress)
		kargs, err := i.kubeinfo.GetKubectlArray("apply", "-f", "-")
		if err != nil {
			return errors.Wrapf(err, "cluster access for install AES")
		}
		fmt.Println("\n$ kubectl apply -f - < [Host Resource]")
		cmd := exec.Command("kubectl", kargs...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Stdin = strings.NewReader(hostResource)
		if err := cmd.Run(); err != nil {
			return errors.Wrap(err, "install AES")
		}

		fmt.Println("\n-> Obtaining a TLS certificate from Let's Encrypt")

		for {
			state, err := i.CaptureKubectl("get Host state", "get", "host", hostname, "-o", "go-template={{.status.state}}")
			if err != nil {
				return err
			}
			if state == "Ready" {
				break
			}
			time.Sleep(500 * time.Millisecond) // FIXME: Time out at some point...
			// FIXME: Do something smart for state == "Error"
		}

		_ = metrics.Report("cert_provisioned")
		fmt.Println("-> TLS configured successfully")
		if err := i.ShowKubectl("show Host", "get", "host", hostname); err != nil {
			return err
		}

		// Open a browser window to the Edge Policy Console
		if err := do_login(kubeinfo, context, "ambassador", hostname, false, false); err != nil {
			return err
		}

	} else {
		fmt.Println("-> Failed to create a DNS name", content)
		//   if reachable from host (e.g., k3s, special case for Minikube?)
		//     open a browser window to http...
		//   else
		//     suggest port-forward to reach policy console?
	}

	_ = metrics.Report("aes_health_good") // or aes_health_bad TODO: Send cluster's install_id and AES version

	return nil
}

type Installer struct {
	kubeinfo *k8s.KubeInfo
}

// Kubernetes Cluster

func (i *Installer) ShowKubectl(name string, args ...string) error {
	kargs, err := i.kubeinfo.GetKubectlArray(args...)
	if err != nil {
		return errors.Wrapf(err, "cluster access for %s", name)
	}
	fmt.Printf("\n$ kubectl %s\n", strings.Join(kargs, " "))
	cmd := exec.Command("kubectl", kargs...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		return errors.Wrap(err, name)
	}
	return nil
}

func (i *Installer) CaptureKubectl(name string, args ...string) (res string, err error) {
	res = ""
	kargs, err := i.kubeinfo.GetKubectlArray(args...)
	if err != nil {
		err = errors.Wrapf(err, "cluster access for %s", name)
		return
	}
	fmt.Printf("\n$ kubectl %s\n", strings.Join(kargs, " "))
	cmd := exec.Command("kubectl", kargs...)
	cmd.Stderr = nil
	resAsBytes, err := cmd.Output()
	if err != nil {
		if ee, ok := err.(*exec.ExitError); ok {
			fmt.Println(ee.Stderr)
		}
		err = errors.Wrap(err, name)
	}
	res = string(resAsBytes)
	return
}

// DNS Registration

type registration struct {
	Email string
	Ip    string
}

// Metrics

type Metrics struct {
	InstallID string
}

func NewMetrics() *Metrics {
	// TODO: Read or create an installation ID
	return nil
}

func (m *Metrics) Report(eventName string) error {
	fmt.Println("-> [Metrics]", eventName)
	return nil
}

const hostManifest = `
apiVersion: getambassador.io/v2
kind: Host
metadata:
  name: %s
  namespace: %s
spec:
  hostname: %s
  acmeProvider:
    email: %s
`
<|endoftext|>


<commit_before>// Copyright 2013 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.

package main

import (
	"bytes"
	"flag"
	"fmt"
	_ "go/importer"
	"go/scanner"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"golang.org/x/tools/imports"

	"sourcegraph.com/sqs/goreturns/returns"
	"github.com/klauspost/asmfmt"
)

var (
	// main operation modes
	list   = flag.Bool("l", false, "list files whose formatting differs from goreturns's")
	write  = flag.Bool("w", false, "write result to (source) file instead of stdout")
	doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")

	goimports = flag.Bool("i", true, "run goimports on the file prior to processing")

	options  = &returns.Options{}
	exitCode = 0
)

func init() {
	flag.BoolVar(&options.PrintErrors, "p", false, "print non-fatal typechecking errors to stderr")
	flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
}

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: goreturns [flags] [path ...]\n")
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "(this version includes asmfmt)\n")
	os.Exit(2)
}

func isGoFile(f os.FileInfo) bool {
	// ignore non-Go files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

func isAsmFile(f os.FileInfo) bool {
	// ignore non-Asm files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".s")
}

func processGoFile(pkgDir, filename string, in io.Reader, out io.Writer, stdin bool) error {
	opt := options
	if stdin {
		nopt := *options
		nopt.Fragment = true
		opt = &nopt
	}

	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	var res = src // This holds the result of processing so far.

	if *goimports {
		var err error
		res, err = imports.Process(filename, res, &imports.Options{
			Fragment:  opt.Fragment,
			AllErrors: opt.AllErrors,
			Comments:  true,
			TabIndent: true,
			TabWidth:  8,
		})
		if err != nil {
			return err
		}
	}

	res, err = returns.Process(pkgDir, filename, res, opt)
	if err != nil {
		return err
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			err = ioutil.WriteFile(filename, res, 0)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			data, err := diff(src, res)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff %s gofmt/%s\n", filename, filename)
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

// If in == nil, the source is the contents of the file with the given filename.
func processAsmFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	res, err := asmfmt.Format(bytes.NewBuffer(src))
	if err != nil {
		return err
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			err = ioutil.WriteFile(filename, res, 0644)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			data, err := diff(src, res)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff %s asmfmt/%s\n", filename, filename)
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

func visitFile(path string, f os.FileInfo, err error) error {
	if err == nil && isGoFile(f) {
		err = processGoFile(filepath.Dir(path), path, nil, os.Stdout, false)
	} else if err == nil && isAsmFile(f) {
		err = processAsmFile(path, nil, os.Stdout, false)
	}
	if err != nil {
		report(err)
	}
	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

func gofmtMain() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		if err := processGoFile("", "<standard input>", os.Stdin, os.Stdout, true); err != nil {
			report(err)
		}
		return
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsDir():
			walkDir(path)
		default:
			if err := visitFile(path, dir, nil); err != nil {
				report(err)
			}
		}
	}
}

func diff(b1, b2 []byte) (data []byte, err error) {
	f1, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(b1)
	f2.Write(b2)

	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		err = nil
	}
	return
}
<commit_msg>Fix imports.<commit_after>// Copyright 2013 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.

package main

import (
	"bytes"
	"flag"
	"fmt"
	_ "go/importer"
	"go/scanner"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"

	"github.com/klauspost/asmfmt"
	"golang.org/x/tools/imports"
	"sourcegraph.com/sqs/goreturns/returns"
)

var (
	// main operation modes
	list   = flag.Bool("l", false, "list files whose formatting differs from goreturns's")
	write  = flag.Bool("w", false, "write result to (source) file instead of stdout")
	doDiff = flag.Bool("d", false, "display diffs instead of rewriting files")

	goimports = flag.Bool("i", true, "run goimports on the file prior to processing")

	options  = &returns.Options{}
	exitCode = 0
)

func init() {
	flag.BoolVar(&options.PrintErrors, "p", false, "print non-fatal typechecking errors to stderr")
	flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)")
}

func report(err error) {
	scanner.PrintError(os.Stderr, err)
	exitCode = 2
}

func usage() {
	fmt.Fprintf(os.Stderr, "usage: goreturns [flags] [path ...]\n")
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "(this version includes asmfmt)\n")
	os.Exit(2)
}

func isGoFile(f os.FileInfo) bool {
	// ignore non-Go files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go")
}

func isAsmFile(f os.FileInfo) bool {
	// ignore non-Asm files
	name := f.Name()
	return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".s")
}

func processGoFile(pkgDir, filename string, in io.Reader, out io.Writer, stdin bool) error {
	opt := options
	if stdin {
		nopt := *options
		nopt.Fragment = true
		opt = &nopt
	}

	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	var res = src // This holds the result of processing so far.

	if *goimports {
		var err error
		res, err = imports.Process(filename, res, &imports.Options{
			Fragment:  opt.Fragment,
			AllErrors: opt.AllErrors,
			Comments:  true,
			TabIndent: true,
			TabWidth:  8,
		})
		if err != nil {
			return err
		}
	}

	res, err = returns.Process(pkgDir, filename, res, opt)
	if err != nil {
		return err
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			err = ioutil.WriteFile(filename, res, 0)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			data, err := diff(src, res)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff %s gofmt/%s\n", filename, filename)
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

// If in == nil, the source is the contents of the file with the given filename.
func processAsmFile(filename string, in io.Reader, out io.Writer, stdin bool) error {
	if in == nil {
		f, err := os.Open(filename)
		if err != nil {
			return err
		}
		defer f.Close()
		in = f
	}

	src, err := ioutil.ReadAll(in)
	if err != nil {
		return err
	}

	res, err := asmfmt.Format(bytes.NewBuffer(src))
	if err != nil {
		return err
	}

	if !bytes.Equal(src, res) {
		// formatting has changed
		if *list {
			fmt.Fprintln(out, filename)
		}
		if *write {
			err = ioutil.WriteFile(filename, res, 0644)
			if err != nil {
				return err
			}
		}
		if *doDiff {
			data, err := diff(src, res)
			if err != nil {
				return fmt.Errorf("computing diff: %s", err)
			}
			fmt.Printf("diff %s asmfmt/%s\n", filename, filename)
			out.Write(data)
		}
	}

	if !*list && !*write && !*doDiff {
		_, err = out.Write(res)
	}

	return err
}

func visitFile(path string, f os.FileInfo, err error) error {
	if err == nil && isGoFile(f) {
		err = processGoFile(filepath.Dir(path), path, nil, os.Stdout, false)
	} else if err == nil && isAsmFile(f) {
		err = processAsmFile(path, nil, os.Stdout, false)
	}
	if err != nil {
		report(err)
	}
	return nil
}

func walkDir(path string) {
	filepath.Walk(path, visitFile)
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	// call gofmtMain in a separate function
	// so that it can use defer and have them
	// run before the exit.
	gofmtMain()
	os.Exit(exitCode)
}

func gofmtMain() {
	flag.Usage = usage
	flag.Parse()

	if flag.NArg() == 0 {
		if err := processGoFile("", "<standard input>", os.Stdin, os.Stdout, true); err != nil {
			report(err)
		}
		return
	}

	for i := 0; i < flag.NArg(); i++ {
		path := flag.Arg(i)
		switch dir, err := os.Stat(path); {
		case err != nil:
			report(err)
		case dir.IsDir():
			walkDir(path)
		default:
			if err := visitFile(path, dir, nil); err != nil {
				report(err)
			}
		}
	}
}

func diff(b1, b2 []byte) (data []byte, err error) {
	f1, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f1.Name())
	defer f1.Close()

	f2, err := ioutil.TempFile("", "gofmt")
	if err != nil {
		return
	}
	defer os.Remove(f2.Name())
	defer f2.Close()

	f1.Write(b1)
	f2.Write(b2)

	data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput()
	if len(data) > 0 {
		// diff exits with a non-zero status when the files don't match.
		// Ignore that failure as long as we get output.
		err = nil
	}
	return
}
<|endoftext|>


<commit_before>package community

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	Name       string   `json:"name" xml:"name"`
	URL        string   `json:"url" xml:"url"`
	Committers int      `json:"committers" xml:"committers"`
	Confidence float64  `json:"confidence" xml:"confidence"`
	OldNames   []string `json:"old_names" xml:"old_names"`
	Stars      int      `json:"stars" xml:"stars"`
}
<commit_msg>Adding fields for repo dates<commit_after>package community

import "time"

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	Name        string    `json:"name" xml:"name"`
	URL         string    `json:"url" xml:"url"`
	Committers  int       `json:"committers" xml:"committers"`
	Confidence  float64   `json:"confidence" xml:"confidence"`
	OldNames    []string  `json:"old_names" xml:"old_names"`
	Stars       int       `json:"stars" xml:"stars"`
	CommittedAt time.Time `json:"committed_at" xml:"committed_at"`
	UpdatedAt   time.Time `json:"updated_at" xml:"updated_at"`
}
<|endoftext|>


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

package backups

import (
	"fmt"

	"github.com/juju/cmd"
	"github.com/juju/errors"

	"github.com/juju/juju/cmd/modelcmd"
)

const removeDoc = `
remove-backup removes a backup from remote storage.
`

// NewRemoveCommand returns a command used to remove a
// backup from remote storage.
func NewRemoveCommand() cmd.Command {
	return modelcmd.Wrap(&removeCommand{})
}

type removeCommand struct {
	CommandBase
	// ID refers to the backup to be removed.
	ID string
}

// Info implements Command.Info.
func (c *removeCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "remove-backup",
		Args:    "<ID>",
		Purpose: "Remove the spcified backup from remote storage.",
		Doc:     removeDoc,
	}
}

// Init implements Command.Init.
func (c *removeCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("missing ID")
	}
	id, args := args[0], args[1:]
	if err := cmd.CheckEmpty(args); err != nil {
		return errors.Trace(err)
	}
	c.ID = id
	return nil
}

// Run implements Command.Run.
func (c *removeCommand) Run(ctx *cmd.Context) error {
	if c.Log != nil {
		if err := c.Log.Start(ctx); err != nil {
			return err
		}
	}
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	err = client.Remove(c.ID)
	if err != nil {
		return errors.Trace(err)
	}

	output := fmt.Sprintf("successfully removed: %v\n", c.ID)
	ctx.Stdout.Write([]byte(output))
	return nil
}
<commit_msg>Fix typo in help.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package backups

import (
	"fmt"

	"github.com/juju/cmd"
	"github.com/juju/errors"

	"github.com/juju/juju/cmd/modelcmd"
)

const removeDoc = `
remove-backup removes a backup from remote storage.
`

// NewRemoveCommand returns a command used to remove a
// backup from remote storage.
func NewRemoveCommand() cmd.Command {
	return modelcmd.Wrap(&removeCommand{})
}

type removeCommand struct {
	CommandBase
	// ID refers to the backup to be removed.
	ID string
}

// Info implements Command.Info.
func (c *removeCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "remove-backup",
		Args:    "<ID>",
		Purpose: "Remove the specified backup from remote storage.",
		Doc:     removeDoc,
	}
}

// Init implements Command.Init.
func (c *removeCommand) Init(args []string) error {
	if len(args) == 0 {
		return errors.New("missing ID")
	}
	id, args := args[0], args[1:]
	if err := cmd.CheckEmpty(args); err != nil {
		return errors.Trace(err)
	}
	c.ID = id
	return nil
}

// Run implements Command.Run.
func (c *removeCommand) Run(ctx *cmd.Context) error {
	if c.Log != nil {
		if err := c.Log.Start(ctx); err != nil {
			return err
		}
	}
	client, err := c.NewAPIClient()
	if err != nil {
		return errors.Trace(err)
	}
	defer client.Close()

	err = client.Remove(c.ID)
	if err != nil {
		return errors.Trace(err)
	}

	output := fmt.Sprintf("successfully removed: %v\n", c.ID)
	ctx.Stdout.Write([]byte(output))
	return nil
}
<|endoftext|>


<commit_before>package community

import (
	"time"
)

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// GetReposInCommonEndpoint is a string representation of the current endpoint for getting repos
	GetReposInCommonEndpoint = `/v1/repo/getReposInCommon`
	// GetReposForActorEndpoint is a string representation of the current endpoint for getting repos
	GetReposForActorEndpoint = `v1/repo/getReposForActor`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	ID            string     `json:"id" xml:"id"`
	Name          string     `json:"name" xml:"name"`
	URL           string     `json:"url" xml:"url"`
	Committers    int        `json:"committers" xml:"committers"`
	TotalActors   int        `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	Confidence    float64    `json:"confidence" xml:"confidence"`
	OldNames      []string   `json:"old_names" xml:"old_names"`
	DefaultBranch string     `json:"default_branch,omitempty" xml:"default_branch,omitempty"`
	MasterBranch  string     `json:"master_branch,omitempty" xml:"master_branch,omitempty"`
	Stars         int        `json:"stars" xml:"stars"`
	CommittedAt   time.Time  `json:"committed_at" xml:"committed_at"`
	UpdatedAt     time.Time  `json:"updated_at" xml:"updated_at"`
	CreatedAt     *time.Time `json:"created_at" xml:"created_at"`
}

// Metrics is a set of data points that represents the measure of a softwares
// community health
type Metrics struct {
	ID                              string          `json:"id" xml:"id"`
	Name                            string          `json:"name" xml:"name"`
	Committers                      int             `json:"committers" xml:"committers"`
	TotalActors                     int             `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	CommittersMonthlyCount          *[]MonthlyCount `json:"committers_monthly_count" xml:"committers_monthly_count"`
	ReleasesTotalCount              *int            `json:"releases_total_count" xml:"releases_total_count"`
	ReleasesMonthlyCount            *[]MonthlyCount `json:"releases_monthly_count" xml:"releases_monthly_count"`
	ReleasesLastAt                  *time.Time      `json:"releases_last_at" xml:"releases_last_at"`
	PullRequestsTotalCount          *int            `json:"pull_requests_total_count" xml:"pull_requests_total_count"`
	PullRequestsLastAt              *time.Time      `json:"pull_requests_last_at" xml:"pull_requests_last_at"`
	PullRequestsMonthlyCount        *[]MonthlyCount `json:"pull_requests_monthly_count" xml:"pull_requests_monthly_count"`
	IssuesLastAt                    *time.Time      `json:"issues_last_at" xml:"issues_last_at"`
	IssuesOpenMonthlyCount          *[]MonthlyCount `json:"issues_open_monthly_count" xml:"issues_open_monthly_count"`
	IssuesClosedMonthlyCount        *[]MonthlyCount `json:"issues_closed_monthly_count" xml:"issues_closed_monthly_count"`
	IssuesClosedMttrMonthly         *[]MonthlyMttr  `json:"issues_closed_mttr_monthly" xml:"issues_closed_mttr_monthly"`
	IssuesClosedMttr                *int            `json:"issues_closed_mttr" xml:"issues_closed_mttr"`
	CommitsTotalCount               *int            `json:"commits_total_count" xml:"commits_total_count"`
	CommitsMonthlyCount             *[]MonthlyCount `json:"commits_monthly_count" xml:"commits_monthly_count"`
	ActorsMonthlyCount              *[]MonthlyCount `json:"actors_monthly_count" xml:"actors_monthly_count"`
	ActionsTotalCount               *int            `json:"actions_total_count" xml:"actions_total_count"`
	ActionsLastAt                   *time.Time      `json:"actions_last_at" xml:"actions_last_at"`
	ActionsFirstAt                  *time.Time      `json:"actions_first_at" xml:"actions_first_at"`
	ActionsMonthlyCount             *[]MonthlyCount `json:"actions_monthly_count" xml:"actions_monthly_count"`
	ContributingActorsTotalCount    *int            `json:"contributing_actors_total_count" xml:"contributing_actors_total_count"`
	ContributingActorsMonthlyCount  *[]MonthlyCount `json:"contributing_actors_monthly_count" xml:"contributing_actors_monthly_count"`
	ContributingActionsTotalCount   *int            `json:"contributing_actions_total_count" xml:"contributing_actions_total_count"`
	ContributingActionsLastAt       *time.Time      `json:"contributing_actions_last_at" xml:"contributing_actions_last_at"`
	ContributingActionsMonthlyCount *[]MonthlyCount `json:"contributing_actions_monthly_count" xml:"contributing_actions_monthly_count"`
	NewActorsMonthlyCount           *[]MonthlyCount `json:"new_actors_monthly_count" xml:"new_actors_monthly_count"`
	MedianWorkingHour               *int            `json:"median_working_hour" xml:"median_working_hour"`
	EOLRearFailingDaysCount         *int            `json:"eol_rear_failing_months_count" xml:"eol_rear_failing_months_count"`
}

// MonthlyCount defines the data needed for month and count
type MonthlyCount struct {
	Month string `json:"month" xml:"month"`
	Count int    `json:"count" xml:"count"`
}

// MonthlyMttr defines the data needed for month and mttr
type MonthlyMttr struct {
	Month string  `json:"month" xml:"month"`
	Mttr  float32 `json:"mttr" xml:"mttr"`
}
<commit_msg>Fix type for mtty<commit_after>package community

import (
	"time"
)

const (
	// GetRepoEndpoint is a string representation of the current endpoint for getting repo
	GetRepoEndpoint = `v1/repo/getRepo`
	// GetReposInCommonEndpoint is a string representation of the current endpoint for getting repos
	GetReposInCommonEndpoint = `/v1/repo/getReposInCommon`
	// GetReposForActorEndpoint is a string representation of the current endpoint for getting repos
	GetReposForActorEndpoint = `v1/repo/getReposForActor`
	// SearchRepoEndpoint is a string representation of the current endpoint for searching repo
	SearchRepoEndpoint = `v1/repo/search`
)

// Repo is a representation of a github repo and corresponding metrics about
// that repo pulled from github
type Repo struct {
	ID            string     `json:"id" xml:"id"`
	Name          string     `json:"name" xml:"name"`
	URL           string     `json:"url" xml:"url"`
	Committers    int        `json:"committers" xml:"committers"`
	TotalActors   int        `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	Confidence    float64    `json:"confidence" xml:"confidence"`
	OldNames      []string   `json:"old_names" xml:"old_names"`
	DefaultBranch string     `json:"default_branch,omitempty" xml:"default_branch,omitempty"`
	MasterBranch  string     `json:"master_branch,omitempty" xml:"master_branch,omitempty"`
	Stars         int        `json:"stars" xml:"stars"`
	CommittedAt   time.Time  `json:"committed_at" xml:"committed_at"`
	UpdatedAt     time.Time  `json:"updated_at" xml:"updated_at"`
	CreatedAt     *time.Time `json:"created_at" xml:"created_at"`
}

// Metrics is a set of data points that represents the measure of a softwares
// community health
type Metrics struct {
	ID                              string          `json:"id" xml:"id"`
	Name                            string          `json:"name" xml:"name"`
	Committers                      int             `json:"committers" xml:"committers"`
	TotalActors                     int             `json:"total_actors,omitempty" xml:"total_actors,omitempty"`
	CommittersMonthlyCount          *[]MonthlyCount `json:"committers_monthly_count" xml:"committers_monthly_count"`
	ReleasesTotalCount              *int            `json:"releases_total_count" xml:"releases_total_count"`
	ReleasesMonthlyCount            *[]MonthlyCount `json:"releases_monthly_count" xml:"releases_monthly_count"`
	ReleasesLastAt                  *time.Time      `json:"releases_last_at" xml:"releases_last_at"`
	PullRequestsTotalCount          *int            `json:"pull_requests_total_count" xml:"pull_requests_total_count"`
	PullRequestsLastAt              *time.Time      `json:"pull_requests_last_at" xml:"pull_requests_last_at"`
	PullRequestsMonthlyCount        *[]MonthlyCount `json:"pull_requests_monthly_count" xml:"pull_requests_monthly_count"`
	IssuesLastAt                    *time.Time      `json:"issues_last_at" xml:"issues_last_at"`
	IssuesOpenMonthlyCount          *[]MonthlyCount `json:"issues_open_monthly_count" xml:"issues_open_monthly_count"`
	IssuesClosedMonthlyCount        *[]MonthlyCount `json:"issues_closed_monthly_count" xml:"issues_closed_monthly_count"`
	IssuesClosedMttrMonthly         *[]MonthlyMttr  `json:"issues_closed_mttr_monthly" xml:"issues_closed_mttr_monthly"`
	IssuesClosedMttr                *float64        `json:"issues_closed_mttr" xml:"issues_closed_mttr"`
	CommitsTotalCount               *int            `json:"commits_total_count" xml:"commits_total_count"`
	CommitsMonthlyCount             *[]MonthlyCount `json:"commits_monthly_count" xml:"commits_monthly_count"`
	ActorsMonthlyCount              *[]MonthlyCount `json:"actors_monthly_count" xml:"actors_monthly_count"`
	ActionsTotalCount               *int            `json:"actions_total_count" xml:"actions_total_count"`
	ActionsLastAt                   *time.Time      `json:"actions_last_at" xml:"actions_last_at"`
	ActionsFirstAt                  *time.Time      `json:"actions_first_at" xml:"actions_first_at"`
	ActionsMonthlyCount             *[]MonthlyCount `json:"actions_monthly_count" xml:"actions_monthly_count"`
	ContributingActorsTotalCount    *int            `json:"contributing_actors_total_count" xml:"contributing_actors_total_count"`
	ContributingActorsMonthlyCount  *[]MonthlyCount `json:"contributing_actors_monthly_count" xml:"contributing_actors_monthly_count"`
	ContributingActionsTotalCount   *int            `json:"contributing_actions_total_count" xml:"contributing_actions_total_count"`
	ContributingActionsLastAt       *time.Time      `json:"contributing_actions_last_at" xml:"contributing_actions_last_at"`
	ContributingActionsMonthlyCount *[]MonthlyCount `json:"contributing_actions_monthly_count" xml:"contributing_actions_monthly_count"`
	NewActorsMonthlyCount           *[]MonthlyCount `json:"new_actors_monthly_count" xml:"new_actors_monthly_count"`
	MedianWorkingHour               *int            `json:"median_working_hour" xml:"median_working_hour"`
	EOLRearFailingDaysCount         *int            `json:"eol_rear_failing_months_count" xml:"eol_rear_failing_months_count"`
}

// MonthlyCount defines the data needed for month and count
type MonthlyCount struct {
	Month string `json:"month" xml:"month"`
	Count int    `json:"count" xml:"count"`
}

// MonthlyMttr defines the data needed for month and mttr
type MonthlyMttr struct {
	Month string  `json:"month" xml:"month"`
	Mttr  float32 `json:"mttr" xml:"mttr"`
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package anago

import (
	"os"
	"path/filepath"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/build"
	"k8s.io/release/pkg/release"
)

// pushCmd represents the subcommand for `krel anago push`
var pushCmd = &cobra.Command{
	Use:   "push",
	Short: "Push release artifacts into the Google Cloud",
	Long: `krel anago push

This subcommand can be used to push the release artifacts to the Google Cloud.
It's only indented to be used from anago, which means the command might be
removed in future releases again when anago goes end of life.
`,
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return errors.Wrap(runPush(pushOpts), "run krel anago push")
	},
}

var (
	pushOpts     = &build.Options{}
	runStage     bool
	runRelease   bool
	buildVersion string
)

func init() {
	pushCmd.PersistentFlags().BoolVar(
		&runStage,
		"stage",
		false,
		"run in stage mode",
	)

	pushCmd.PersistentFlags().BoolVar(
		&runRelease,
		"release",
		false,
		"run in release mode",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.Version,
		"version",
		"",
		"version to be used",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.BuildDir,
		"build-dir",
		"",
		"build artifact directory of the release",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.Bucket,
		"bucket",
		"",
		"GCS bucket to be used",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.Registry,
		"container-registry",
		"",
		"Container image registry to be used",
	)

	pushCmd.PersistentFlags().StringVar(
		&buildVersion,
		"build-version",
		"",
		"Build version from Jenkins (only used when --release specified)",
	)

	pushOpts.AllowDup = true
	pushOpts.ValidateRemoteImageDigests = true

	AnagoCmd.AddCommand(pushCmd)
}

func runPush(opts *build.Options) error {
	buildInstance := build.NewInstance(opts)
	if err := buildInstance.CheckReleaseBucket(); err != nil {
		return errors.Wrap(err, "check release bucket access")
	}

	if runStage {
		return runPushStage(buildInstance, opts)
	} else if runRelease {
		return runPushRelease(buildInstance, opts)
	}

	return errors.New("neither --stage nor --release provided")
}

func runPushStage(
	buildInstance *build.Instance,
	opts *build.Options,
) error {
	workDir := os.Getenv("GOPATH")
	if workDir == "" {
		return errors.New("GOPATH is not set")
	}

	// Stage the local source tree
	if err := buildInstance.StageLocalSourceTree(workDir, buildVersion); err != nil {
		return errors.Wrap(err, "staging local source tree")
	}

	// Stage local artifacts and write checksums
	if err := buildInstance.StageLocalArtifacts(); err != nil {
		return errors.Wrap(err, "staging local artifacts")
	}
	gcsPath := filepath.Join("stage", buildVersion, opts.Version)

	// Push gcs-stage to GCS
	if err := buildInstance.PushReleaseArtifacts(
		filepath.Join(opts.BuildDir, release.GCSStagePath, opts.Version),
		filepath.Join(gcsPath, release.GCSStagePath, opts.Version),
	); err != nil {
		return errors.Wrap(err, "pushing release artifacts")
	}

	// Push container release-images to GCS
	if err := buildInstance.PushReleaseArtifacts(
		filepath.Join(opts.BuildDir, release.ImagesPath),
		filepath.Join(gcsPath, release.ImagesPath),
	); err != nil {
		return errors.Wrap(err, "pushing release artifacts")
	}

	// Push container images into registry
	if err := buildInstance.PushContainerImages(); err != nil {
		return errors.Wrap(err, "pushing container images")
	}

	return nil
}

func runPushRelease(
	buildInstance *build.Instance,
	opts *build.Options,
) error {
	if err := buildInstance.CopyStagedFromGCS(opts.Bucket, buildVersion); err != nil {
		return errors.Wrap(err, "copy staged from GCS")
	}

	// In an official nomock release, we want to ensure that container images
	// have been promoted from staging to production, so we do the image
	// manifest validation against production instead of staging.
	targetRegistry := opts.Registry
	if targetRegistry == release.GCRIOPathStaging {
		targetRegistry = release.GCRIOPathProd
	}
	// Image promotion has been done on nomock stage, verify that the images
	// are available.
	if err := release.NewImages().Validate(
		targetRegistry, opts.Version, opts.BuildDir,
	); err != nil {
		return errors.Wrap(err, "validate container images")
	}

	if err := release.NewPublisher().PublishVersion(
		"release", opts.Version, opts.BuildDir, opts.Bucket, "", nil, false, false,
	); err != nil {
		return errors.Wrap(err, "publish release")
	}
	return nil
}
<commit_msg>Add GCS bucket to release artifacts push<commit_after>/*
Copyright 2020 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package anago

import (
	"os"
	"path/filepath"

	"github.com/pkg/errors"
	"github.com/spf13/cobra"

	"k8s.io/release/pkg/build"
	"k8s.io/release/pkg/release"
)

// pushCmd represents the subcommand for `krel anago push`
var pushCmd = &cobra.Command{
	Use:   "push",
	Short: "Push release artifacts into the Google Cloud",
	Long: `krel anago push

This subcommand can be used to push the release artifacts to the Google Cloud.
It's only indented to be used from anago, which means the command might be
removed in future releases again when anago goes end of life.
`,
	SilenceUsage:  true,
	SilenceErrors: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return errors.Wrap(runPush(pushOpts), "run krel anago push")
	},
}

var (
	pushOpts     = &build.Options{}
	runStage     bool
	runRelease   bool
	buildVersion string
)

func init() {
	pushCmd.PersistentFlags().BoolVar(
		&runStage,
		"stage",
		false,
		"run in stage mode",
	)

	pushCmd.PersistentFlags().BoolVar(
		&runRelease,
		"release",
		false,
		"run in release mode",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.Version,
		"version",
		"",
		"version to be used",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.BuildDir,
		"build-dir",
		"",
		"build artifact directory of the release",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.Bucket,
		"bucket",
		"",
		"GCS bucket to be used",
	)

	pushCmd.PersistentFlags().StringVar(
		&pushOpts.Registry,
		"container-registry",
		"",
		"Container image registry to be used",
	)

	pushCmd.PersistentFlags().StringVar(
		&buildVersion,
		"build-version",
		"",
		"Build version from Jenkins (only used when --release specified)",
	)

	pushOpts.AllowDup = true
	pushOpts.ValidateRemoteImageDigests = true

	AnagoCmd.AddCommand(pushCmd)
}

func runPush(opts *build.Options) error {
	buildInstance := build.NewInstance(opts)
	if err := buildInstance.CheckReleaseBucket(); err != nil {
		return errors.Wrap(err, "check release bucket access")
	}

	if runStage {
		return runPushStage(buildInstance, opts)
	} else if runRelease {
		return runPushRelease(buildInstance, opts)
	}

	return errors.New("neither --stage nor --release provided")
}

func runPushStage(
	buildInstance *build.Instance,
	opts *build.Options,
) error {
	workDir := os.Getenv("GOPATH")
	if workDir == "" {
		return errors.New("GOPATH is not set")
	}

	// Stage the local source tree
	if err := buildInstance.StageLocalSourceTree(workDir, buildVersion); err != nil {
		return errors.Wrap(err, "staging local source tree")
	}

	// Stage local artifacts and write checksums
	if err := buildInstance.StageLocalArtifacts(); err != nil {
		return errors.Wrap(err, "staging local artifacts")
	}
	gcsPath := filepath.Join(opts.Bucket, "stage", buildVersion, opts.Version)

	// Push gcs-stage to GCS
	if err := buildInstance.PushReleaseArtifacts(
		filepath.Join(opts.BuildDir, release.GCSStagePath, opts.Version),
		filepath.Join(gcsPath, release.GCSStagePath, opts.Version),
	); err != nil {
		return errors.Wrap(err, "pushing release artifacts")
	}

	// Push container release-images to GCS
	if err := buildInstance.PushReleaseArtifacts(
		filepath.Join(opts.BuildDir, release.ImagesPath),
		filepath.Join(gcsPath, release.ImagesPath),
	); err != nil {
		return errors.Wrap(err, "pushing release artifacts")
	}

	// Push container images into registry
	if err := buildInstance.PushContainerImages(); err != nil {
		return errors.Wrap(err, "pushing container images")
	}

	return nil
}

func runPushRelease(
	buildInstance *build.Instance,
	opts *build.Options,
) error {
	if err := buildInstance.CopyStagedFromGCS(opts.Bucket, buildVersion); err != nil {
		return errors.Wrap(err, "copy staged from GCS")
	}

	// In an official nomock release, we want to ensure that container images
	// have been promoted from staging to production, so we do the image
	// manifest validation against production instead of staging.
	targetRegistry := opts.Registry
	if targetRegistry == release.GCRIOPathStaging {
		targetRegistry = release.GCRIOPathProd
	}
	// Image promotion has been done on nomock stage, verify that the images
	// are available.
	if err := release.NewImages().Validate(
		targetRegistry, opts.Version, opts.BuildDir,
	); err != nil {
		return errors.Wrap(err, "validate container images")
	}

	if err := release.NewPublisher().PublishVersion(
		"release", opts.Version, opts.BuildDir, opts.Bucket, "", nil, false, false,
	); err != nil {
		return errors.Wrap(err, "publish release")
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2019 VMware, Inc. All Rights Reserved.

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 simulator

import (
	"context"
	"reflect"

	"github.com/google/uuid"
	"github.com/vmware/govmomi/cns"
	"github.com/vmware/govmomi/cns/methods"
	cnstypes "github.com/vmware/govmomi/cns/types"
	"github.com/vmware/govmomi/simulator"
	"github.com/vmware/govmomi/vim25/soap"
	vim25types "github.com/vmware/govmomi/vim25/types"
)

func New() *simulator.Registry {
	r := simulator.NewRegistry()
	r.Namespace = cns.Namespace
	r.Path = cns.Path

	r.Put(&CnsVolumeManager{
		ManagedObjectReference: cns.CnsVolumeManagerInstance,
		volumes:                make(map[vim25types.ManagedObjectReference]map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume),
		attachments:            make(map[cnstypes.CnsVolumeId]vim25types.ManagedObjectReference),
	})

	return r
}

type CnsVolumeManager struct {
	vim25types.ManagedObjectReference
	volumes     map[vim25types.ManagedObjectReference]map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume
	attachments map[cnstypes.CnsVolumeId]vim25types.ManagedObjectReference
}

const simulatorDiskUUID = "6000c298595bf4575739e9105b2c0c2d"

func (m *CnsVolumeManager) CnsCreateVolume(ctx context.Context, req *cnstypes.CnsCreateVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsCreateVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.CreateSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsVolumeCreateSpec"}
		}

		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, createSpec := range req.CreateSpecs {
			staticProvisionedSpec, ok := interface{}(createSpec.BackingObjectDetails).(*cnstypes.CnsBlockBackingDetails)
			if ok && staticProvisionedSpec.BackingDiskId != "" {
				datastore := simulator.Map.Any("Datastore").(*simulator.Datastore)
				volumes, ok := m.volumes[datastore.Self]
				if !ok {
					volumes = make(map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume)
					m.volumes[datastore.Self] = volumes
				}
				newVolume := &cnstypes.CnsVolume{
					VolumeId: cnstypes.CnsVolumeId{
						Id: interface{}(createSpec.BackingObjectDetails).(*cnstypes.CnsBlockBackingDetails).BackingDiskId,
					},
					Name:                         createSpec.Name,
					VolumeType:                   createSpec.VolumeType,
					DatastoreUrl:                 datastore.Info.GetDatastoreInfo().Url,
					Metadata:                     createSpec.Metadata,
					BackingObjectDetails:         createSpec.BackingObjectDetails.(cnstypes.BaseCnsBackingObjectDetails).GetCnsBackingObjectDetails(),
					ComplianceStatus:             "Simulator Compliance Status",
					DatastoreAccessibilityStatus: "Simulator Datastore Accessibility Status",
				}

				volumes[newVolume.VolumeId] = newVolume
				operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
					VolumeId: newVolume.VolumeId,
				})

			} else {
				for _, datastoreRef := range createSpec.Datastores {
					datastore := simulator.Map.Get(datastoreRef).(*simulator.Datastore)

					volumes, ok := m.volumes[datastore.Self]
					if !ok {
						volumes = make(map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume)
						m.volumes[datastore.Self] = volumes

					}

					var policyId string
					if createSpec.Profile != nil && createSpec.Profile[0] != nil &&
						reflect.TypeOf(createSpec.Profile[0]) == reflect.TypeOf(&vim25types.VirtualMachineDefinedProfileSpec{}) {
						policyId = interface{}(createSpec.Profile[0]).(*vim25types.VirtualMachineDefinedProfileSpec).ProfileId
					}

					newVolume := &cnstypes.CnsVolume{
						VolumeId: cnstypes.CnsVolumeId{
							Id: uuid.New().String(),
						},
						Name:                         createSpec.Name,
						VolumeType:                   createSpec.VolumeType,
						DatastoreUrl:                 datastore.Info.GetDatastoreInfo().Url,
						Metadata:                     createSpec.Metadata,
						BackingObjectDetails:         createSpec.BackingObjectDetails.(cnstypes.BaseCnsBackingObjectDetails).GetCnsBackingObjectDetails(),
						ComplianceStatus:             "Simulator Compliance Status",
						DatastoreAccessibilityStatus: "Simulator Datastore Accessibility Status",
						StoragePolicyId:              policyId,
					}

					volumes[newVolume.VolumeId] = newVolume
					operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
						VolumeId: newVolume.VolumeId,
					})
				}
			}
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsCreateVolumeBody{
		Res: &cnstypes.CnsCreateVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsQueryVolume simulates the query volumes implementation for CNSQuery API
func (m *CnsVolumeManager) CnsQueryVolume(ctx context.Context, req *cnstypes.CnsQueryVolume) soap.HasFault {
	retVolumes := []cnstypes.CnsVolume{}
	reqVolumeIds := make(map[string]bool)
	isQueryFilter := false

	if req.Filter.VolumeIds != nil {
		isQueryFilter = true
	}
	// Create map of requested volume Ids in query request
	for _, volumeID := range req.Filter.VolumeIds {
		reqVolumeIds[volumeID.Id] = true
	}

	for _, dsVolumes := range m.volumes {
		for _, volume := range dsVolumes {
			if isQueryFilter {
				if _, ok := reqVolumeIds[volume.VolumeId.Id]; ok {
					retVolumes = append(retVolumes, *volume)
				}
			} else {
				retVolumes = append(retVolumes, *volume)
			}
		}
	}

	return &methods.CnsQueryVolumeBody{
		Res: &cnstypes.CnsQueryVolumeResponse{
			Returnval: cnstypes.CnsQueryResult{
				Volumes: retVolumes,
				Cursor:  cnstypes.CnsCursor{},
			},
		},
	}
}

// CnsQueryAllVolume simulates the query volumes implementation for CNSQueryAll API
func (m *CnsVolumeManager) CnsQueryAllVolume(ctx context.Context, req *cnstypes.CnsQueryAllVolume) soap.HasFault {
	retVolumes := []cnstypes.CnsVolume{}
	reqVolumeIds := make(map[string]bool)
	isQueryFilter := false

	if req.Filter.VolumeIds != nil {
		isQueryFilter = true
	}
	// Create map of requested volume Ids in query request
	for _, volumeID := range req.Filter.VolumeIds {
		reqVolumeIds[volumeID.Id] = true
	}

	for _, dsVolumes := range m.volumes {
		for _, volume := range dsVolumes {
			if isQueryFilter {
				if _, ok := reqVolumeIds[volume.VolumeId.Id]; ok {
					retVolumes = append(retVolumes, *volume)
				}
			} else {
				retVolumes = append(retVolumes, *volume)
			}
		}
	}

	return &methods.CnsQueryAllVolumeBody{
		Res: &cnstypes.CnsQueryAllVolumeResponse{
			Returnval: cnstypes.CnsQueryResult{
				Volumes: retVolumes,
				Cursor:  cnstypes.CnsCursor{},
			},
		},
	}
}

func (m *CnsVolumeManager) CnsDeleteVolume(ctx context.Context, req *cnstypes.CnsDeleteVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsDeleteVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, volumeId := range req.VolumeIds {
			for ds, dsVolumes := range m.volumes {
				volume := dsVolumes[volumeId]
				if volume != nil {
					delete(m.volumes[ds], volumeId)
					operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
						VolumeId: volumeId,
					})

				}
			}
		}
		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsDeleteVolumeBody{
		Res: &cnstypes.CnsDeleteVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsUpdateVolumeMetadata simulates UpdateVolumeMetadata call for simulated vc
func (m *CnsVolumeManager) CnsUpdateVolumeMetadata(ctx context.Context, req *cnstypes.CnsUpdateVolumeMetadata) soap.HasFault {
	task := simulator.CreateTask(m, "CnsUpdateVolumeMetadata", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.UpdateSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsUpdateVolumeMetadataSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, updateSpecs := range req.UpdateSpecs {
			for _, dsVolumes := range m.volumes {
				for id, volume := range dsVolumes {
					if id.Id == updateSpecs.VolumeId.Id {
						volume.Metadata.EntityMetadata = updateSpecs.Metadata.EntityMetadata
						operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
							VolumeId: volume.VolumeId,
						})
						break
					}
				}
			}

		}
		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})
	return &methods.CnsUpdateVolumeBody{
		Res: &cnstypes.CnsUpdateVolumeMetadataResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsAttachVolume simulates AttachVolume call for simulated vc
func (m *CnsVolumeManager) CnsAttachVolume(ctx context.Context, req *cnstypes.CnsAttachVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsAttachVolume", func(task *simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.AttachSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsAttachVolumeSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, attachSpec := range req.AttachSpecs {
			node := simulator.Map.Get(attachSpec.Vm).(*simulator.VirtualMachine)
			if _, ok := m.attachments[attachSpec.VolumeId]; !ok {
				m.attachments[attachSpec.VolumeId] = node.Self
			} else {
				return nil, &vim25types.ResourceInUse{
					Name: attachSpec.VolumeId.Id,
				}
			}
			operationResult = append(operationResult, &cnstypes.CnsVolumeAttachResult{
				CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
					VolumeId: attachSpec.VolumeId,
				},
				DiskUUID: simulatorDiskUUID,
			})
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsAttachVolumeBody{
		Res: &cnstypes.CnsAttachVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsDetachVolume simulates DetachVolume call for simulated vc
func (m *CnsVolumeManager) CnsDetachVolume(ctx context.Context, req *cnstypes.CnsDetachVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsDetachVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.DetachSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsDetachVolumeSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, detachSpec := range req.DetachSpecs {
			if _, ok := m.attachments[detachSpec.VolumeId]; ok {
				delete(m.attachments, detachSpec.VolumeId)
				operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
					VolumeId: detachSpec.VolumeId,
				})
			} else {
				return nil, &vim25types.InvalidArgument{
					InvalidProperty: detachSpec.VolumeId.Id,
				}
			}
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})
	return &methods.CnsDetachVolumeBody{
		Res: &cnstypes.CnsDetachVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsExtendVolume simulates ExtendVolume call for simulated vc
func (m *CnsVolumeManager) CnsExtendVolume(ctx context.Context, req *cnstypes.CnsExtendVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsExtendVolume", func(task *simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.ExtendSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsExtendVolumeSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}

		for _, extendSpecs := range req.ExtendSpecs {
			for _, dsVolumes := range m.volumes {
				for id, volume := range dsVolumes {
					if id.Id == extendSpecs.VolumeId.Id {
						volume.BackingObjectDetails = &cnstypes.CnsBackingObjectDetails{
							CapacityInMb: extendSpecs.CapacityInMb,
						}
						operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
							VolumeId: volume.VolumeId,
						})
						break
					}
				}
			}
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsExtendVolumeBody{
		Res: &cnstypes.CnsExtendVolumeResponse{
			Returnval: task.Run(),
		},
	}
}
<commit_msg>Add logic to return default HealthStatus in CnsCreateVolume.<commit_after>/*
Copyright (c) 2019 VMware, Inc. All Rights Reserved.

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 simulator

import (
	"context"
	"reflect"

	"github.com/google/uuid"
	"github.com/vmware/govmomi/cns"
	"github.com/vmware/govmomi/cns/methods"
	cnstypes "github.com/vmware/govmomi/cns/types"
	pbmtypes "github.com/vmware/govmomi/pbm/types"
	"github.com/vmware/govmomi/simulator"
	"github.com/vmware/govmomi/vim25/soap"
	vim25types "github.com/vmware/govmomi/vim25/types"
)

func New() *simulator.Registry {
	r := simulator.NewRegistry()
	r.Namespace = cns.Namespace
	r.Path = cns.Path

	r.Put(&CnsVolumeManager{
		ManagedObjectReference: cns.CnsVolumeManagerInstance,
		volumes:                make(map[vim25types.ManagedObjectReference]map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume),
		attachments:            make(map[cnstypes.CnsVolumeId]vim25types.ManagedObjectReference),
	})

	return r
}

type CnsVolumeManager struct {
	vim25types.ManagedObjectReference
	volumes     map[vim25types.ManagedObjectReference]map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume
	attachments map[cnstypes.CnsVolumeId]vim25types.ManagedObjectReference
}

const simulatorDiskUUID = "6000c298595bf4575739e9105b2c0c2d"

func (m *CnsVolumeManager) CnsCreateVolume(ctx context.Context, req *cnstypes.CnsCreateVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsCreateVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.CreateSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsVolumeCreateSpec"}
		}

		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, createSpec := range req.CreateSpecs {
			staticProvisionedSpec, ok := interface{}(createSpec.BackingObjectDetails).(*cnstypes.CnsBlockBackingDetails)
			if ok && staticProvisionedSpec.BackingDiskId != "" {
				datastore := simulator.Map.Any("Datastore").(*simulator.Datastore)
				volumes, ok := m.volumes[datastore.Self]
				if !ok {
					volumes = make(map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume)
					m.volumes[datastore.Self] = volumes
				}
				newVolume := &cnstypes.CnsVolume{
					VolumeId: cnstypes.CnsVolumeId{
						Id: interface{}(createSpec.BackingObjectDetails).(*cnstypes.CnsBlockBackingDetails).BackingDiskId,
					},
					Name:                         createSpec.Name,
					VolumeType:                   createSpec.VolumeType,
					DatastoreUrl:                 datastore.Info.GetDatastoreInfo().Url,
					Metadata:                     createSpec.Metadata,
					BackingObjectDetails:         createSpec.BackingObjectDetails.(cnstypes.BaseCnsBackingObjectDetails).GetCnsBackingObjectDetails(),
					ComplianceStatus:             "Simulator Compliance Status",
					DatastoreAccessibilityStatus: "Simulator Datastore Accessibility Status",
					HealthStatus:                 string(pbmtypes.PbmHealthStatusForEntityGreen),
				}

				volumes[newVolume.VolumeId] = newVolume
				operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
					VolumeId: newVolume.VolumeId,
				})

			} else {
				for _, datastoreRef := range createSpec.Datastores {
					datastore := simulator.Map.Get(datastoreRef).(*simulator.Datastore)

					volumes, ok := m.volumes[datastore.Self]
					if !ok {
						volumes = make(map[cnstypes.CnsVolumeId]*cnstypes.CnsVolume)
						m.volumes[datastore.Self] = volumes

					}

					var policyId string
					if createSpec.Profile != nil && createSpec.Profile[0] != nil &&
						reflect.TypeOf(createSpec.Profile[0]) == reflect.TypeOf(&vim25types.VirtualMachineDefinedProfileSpec{}) {
						policyId = interface{}(createSpec.Profile[0]).(*vim25types.VirtualMachineDefinedProfileSpec).ProfileId
					}

					newVolume := &cnstypes.CnsVolume{
						VolumeId: cnstypes.CnsVolumeId{
							Id: uuid.New().String(),
						},
						Name:                         createSpec.Name,
						VolumeType:                   createSpec.VolumeType,
						DatastoreUrl:                 datastore.Info.GetDatastoreInfo().Url,
						Metadata:                     createSpec.Metadata,
						BackingObjectDetails:         createSpec.BackingObjectDetails.(cnstypes.BaseCnsBackingObjectDetails).GetCnsBackingObjectDetails(),
						ComplianceStatus:             "Simulator Compliance Status",
						DatastoreAccessibilityStatus: "Simulator Datastore Accessibility Status",
						HealthStatus:                 string(pbmtypes.PbmHealthStatusForEntityGreen),
						StoragePolicyId:              policyId,
					}

					volumes[newVolume.VolumeId] = newVolume
					operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
						VolumeId: newVolume.VolumeId,
					})
				}
			}
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsCreateVolumeBody{
		Res: &cnstypes.CnsCreateVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsQueryVolume simulates the query volumes implementation for CNSQuery API
func (m *CnsVolumeManager) CnsQueryVolume(ctx context.Context, req *cnstypes.CnsQueryVolume) soap.HasFault {
	retVolumes := []cnstypes.CnsVolume{}
	reqVolumeIds := make(map[string]bool)
	isQueryFilter := false

	if req.Filter.VolumeIds != nil {
		isQueryFilter = true
	}
	// Create map of requested volume Ids in query request
	for _, volumeID := range req.Filter.VolumeIds {
		reqVolumeIds[volumeID.Id] = true
	}

	for _, dsVolumes := range m.volumes {
		for _, volume := range dsVolumes {
			if isQueryFilter {
				if _, ok := reqVolumeIds[volume.VolumeId.Id]; ok {
					retVolumes = append(retVolumes, *volume)
				}
			} else {
				retVolumes = append(retVolumes, *volume)
			}
		}
	}

	return &methods.CnsQueryVolumeBody{
		Res: &cnstypes.CnsQueryVolumeResponse{
			Returnval: cnstypes.CnsQueryResult{
				Volumes: retVolumes,
				Cursor:  cnstypes.CnsCursor{},
			},
		},
	}
}

// CnsQueryAllVolume simulates the query volumes implementation for CNSQueryAll API
func (m *CnsVolumeManager) CnsQueryAllVolume(ctx context.Context, req *cnstypes.CnsQueryAllVolume) soap.HasFault {
	retVolumes := []cnstypes.CnsVolume{}
	reqVolumeIds := make(map[string]bool)
	isQueryFilter := false

	if req.Filter.VolumeIds != nil {
		isQueryFilter = true
	}
	// Create map of requested volume Ids in query request
	for _, volumeID := range req.Filter.VolumeIds {
		reqVolumeIds[volumeID.Id] = true
	}

	for _, dsVolumes := range m.volumes {
		for _, volume := range dsVolumes {
			if isQueryFilter {
				if _, ok := reqVolumeIds[volume.VolumeId.Id]; ok {
					retVolumes = append(retVolumes, *volume)
				}
			} else {
				retVolumes = append(retVolumes, *volume)
			}
		}
	}

	return &methods.CnsQueryAllVolumeBody{
		Res: &cnstypes.CnsQueryAllVolumeResponse{
			Returnval: cnstypes.CnsQueryResult{
				Volumes: retVolumes,
				Cursor:  cnstypes.CnsCursor{},
			},
		},
	}
}

func (m *CnsVolumeManager) CnsDeleteVolume(ctx context.Context, req *cnstypes.CnsDeleteVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsDeleteVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, volumeId := range req.VolumeIds {
			for ds, dsVolumes := range m.volumes {
				volume := dsVolumes[volumeId]
				if volume != nil {
					delete(m.volumes[ds], volumeId)
					operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
						VolumeId: volumeId,
					})

				}
			}
		}
		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsDeleteVolumeBody{
		Res: &cnstypes.CnsDeleteVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsUpdateVolumeMetadata simulates UpdateVolumeMetadata call for simulated vc
func (m *CnsVolumeManager) CnsUpdateVolumeMetadata(ctx context.Context, req *cnstypes.CnsUpdateVolumeMetadata) soap.HasFault {
	task := simulator.CreateTask(m, "CnsUpdateVolumeMetadata", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.UpdateSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsUpdateVolumeMetadataSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, updateSpecs := range req.UpdateSpecs {
			for _, dsVolumes := range m.volumes {
				for id, volume := range dsVolumes {
					if id.Id == updateSpecs.VolumeId.Id {
						volume.Metadata.EntityMetadata = updateSpecs.Metadata.EntityMetadata
						operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
							VolumeId: volume.VolumeId,
						})
						break
					}
				}
			}

		}
		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})
	return &methods.CnsUpdateVolumeBody{
		Res: &cnstypes.CnsUpdateVolumeMetadataResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsAttachVolume simulates AttachVolume call for simulated vc
func (m *CnsVolumeManager) CnsAttachVolume(ctx context.Context, req *cnstypes.CnsAttachVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsAttachVolume", func(task *simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.AttachSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsAttachVolumeSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, attachSpec := range req.AttachSpecs {
			node := simulator.Map.Get(attachSpec.Vm).(*simulator.VirtualMachine)
			if _, ok := m.attachments[attachSpec.VolumeId]; !ok {
				m.attachments[attachSpec.VolumeId] = node.Self
			} else {
				return nil, &vim25types.ResourceInUse{
					Name: attachSpec.VolumeId.Id,
				}
			}
			operationResult = append(operationResult, &cnstypes.CnsVolumeAttachResult{
				CnsVolumeOperationResult: cnstypes.CnsVolumeOperationResult{
					VolumeId: attachSpec.VolumeId,
				},
				DiskUUID: simulatorDiskUUID,
			})
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsAttachVolumeBody{
		Res: &cnstypes.CnsAttachVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsDetachVolume simulates DetachVolume call for simulated vc
func (m *CnsVolumeManager) CnsDetachVolume(ctx context.Context, req *cnstypes.CnsDetachVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsDetachVolume", func(*simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.DetachSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsDetachVolumeSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}
		for _, detachSpec := range req.DetachSpecs {
			if _, ok := m.attachments[detachSpec.VolumeId]; ok {
				delete(m.attachments, detachSpec.VolumeId)
				operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
					VolumeId: detachSpec.VolumeId,
				})
			} else {
				return nil, &vim25types.InvalidArgument{
					InvalidProperty: detachSpec.VolumeId.Id,
				}
			}
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})
	return &methods.CnsDetachVolumeBody{
		Res: &cnstypes.CnsDetachVolumeResponse{
			Returnval: task.Run(),
		},
	}
}

// CnsExtendVolume simulates ExtendVolume call for simulated vc
func (m *CnsVolumeManager) CnsExtendVolume(ctx context.Context, req *cnstypes.CnsExtendVolume) soap.HasFault {
	task := simulator.CreateTask(m, "CnsExtendVolume", func(task *simulator.Task) (vim25types.AnyType, vim25types.BaseMethodFault) {
		if len(req.ExtendSpecs) == 0 {
			return nil, &vim25types.InvalidArgument{InvalidProperty: "CnsExtendVolumeSpec"}
		}
		operationResult := []cnstypes.BaseCnsVolumeOperationResult{}

		for _, extendSpecs := range req.ExtendSpecs {
			for _, dsVolumes := range m.volumes {
				for id, volume := range dsVolumes {
					if id.Id == extendSpecs.VolumeId.Id {
						volume.BackingObjectDetails = &cnstypes.CnsBackingObjectDetails{
							CapacityInMb: extendSpecs.CapacityInMb,
						}
						operationResult = append(operationResult, &cnstypes.CnsVolumeOperationResult{
							VolumeId: volume.VolumeId,
						})
						break
					}
				}
			}
		}

		return &cnstypes.CnsVolumeOperationBatchResult{
			VolumeResults: operationResult,
		}, nil
	})

	return &methods.CnsExtendVolumeBody{
		Res: &cnstypes.CnsExtendVolumeResponse{
			Returnval: task.Run(),
		},
	}
}
<|endoftext|>


<commit_before>/*
xslate is an extremely powerful template engine, based on Perl5's Text::Xslate
module. Xslate uses a virtual machine to execute pre-compiled template bytecode,
which gives its flexibility while maitaining a very fast execution speed.

Note that RenderString() DOES NOT CACHE THE GENERATED BYTECODE. This has
significant effect on performance if you repeatedly call the same template

*/
package xslate

import (
  "errors"
  "fmt"
  "io/ioutil"
  "os"
  "reflect"

  "github.com/lestrrat/go-xslate/compiler"
  "github.com/lestrrat/go-xslate/loader"
  "github.com/lestrrat/go-xslate/parser"
  "github.com/lestrrat/go-xslate/parser/tterse"
  "github.com/lestrrat/go-xslate/vm"
)

type Vars vm.Vars
type Xslate struct {
  Flags    int32
  Vm       *vm.VM
  Compiler compiler.Compiler
  Parser   parser.Parser
  Loader   loader.ByteCodeLoader
  // XXX Need to make syntax pluggable
}

type ConfigureArgs interface {
  Get(string) (interface {}, bool)
}

type Args map[string]interface {}

func DefaultCompiler(tx *Xslate, args Args) error {
  tx.Compiler = compiler.New()
  return nil
}

func DefaultParser(tx *Xslate, args Args) error {
  tx.Parser = tterse.New()
  return nil
}

func DefaultLoader(tx *Xslate, args Args) error {
  var tmp interface {}
  tmp, ok := args.Get("CacheDir")
  if !ok {
    tmp, _ = ioutil.TempDir("", "go-xslate-cache-")
  }
  cacheDir := tmp.(string)

  tmp, ok = args.Get("LoadPaths")
  if !ok {
    cwd, _ := os.Getwd()
    tmp = []string { cwd }
  }
  paths := tmp.([]string)

  cache, err := loader.NewFileCache(cacheDir)
  if err != nil {
    return err
  }
  fileloader, err := loader.NewFileTemplateLoader(paths)
  if err != nil {
    return err
  }
  tx.Loader = loader.NewCachedByteCodeLoader(cache, fileloader, tx.Parser, tx.Compiler)
  return nil
}

func DefaultVm(tx *Xslate, args Args) error {
  tx.Vm = vm.NewVM()
  tx.Vm.Loader = tx.Loader
  return nil
}

func (args Args) Get(key string) (interface {}, bool) {
  ret, ok := args[key]
  return ret, ok
}

func (tx *Xslate) configureGeneric(configuror interface {}, args Args) error {
  ref := reflect.ValueOf(configuror)
  switch ref.Type().Kind() {
  case reflect.Func:
    // If this is a function, it better take our Xslate instance as the
    // sole argument, and initialize it as it pleases
    if ref.Type().NumIn() != 2 && (ref.Type().In(0).Name() != "Xslate" || ref.Type().In(1).Name() != "Args") {
      panic(fmt.Sprintf(`Expected function initializer "func (tx *Xslate ", but instead of %s`, ref.Type))
    }
    cb := configuror.(func(*Xslate, Args) error)
    err := cb(tx, args)
    return err
  }
  return errors.New("Bad configurator")
}

func (tx *Xslate) Configure(args ConfigureArgs) error {
  // The compiler currently does not have any configurable options, but
  // one may want to replace the entire compiler struct
  defaults := map[string]func(*Xslate, Args) error {
    "Compiler": DefaultCompiler,
    "Parser":   DefaultParser,
    "Loader":   DefaultLoader,
    "Vm":       DefaultVm,
  }

  for _, key := range []string { "Parser", "Compiler", "Loader", "Vm" } {
    configKey := "Configure" + key
    configuror, ok := args.Get(configKey);
    if !ok {
      configuror = defaults[key]
    }

    args, ok := args.Get(key)
    if !ok {
      args = Args {}
    }

    err := tx.configureGeneric(configuror, args.(Args))
    if err != nil {
      return err
    }
  }

  return nil
}

func New(args ...Args) (*Xslate, error) {
  tx := &Xslate {}

  // We jump through hoops because there are A LOT of configuration options
  // but most of them only need to use the default values
  if len(args) <= 0 {
    args = []Args { Args {} }
  }
  err := tx.Configure(args[0])
  if err != nil {
    return nil, err
  }
  return tx, nil
}

func (tx *Xslate) DumpAST(b bool) {
  tx.Loader.DumpAST(b)
}

func (tx *Xslate) DumpByteCode(b bool) {
  tx.Loader.DumpByteCode(b)
}

func (x *Xslate) Render(name string, vars Vars) (string, error) {
  bc, err := x.Loader.Load(name)
  if err != nil {
    return "", err
  }
  x.Vm.Run(bc, vm.Vars(vars))
  str, err := x.Vm.OutputString()
  return str, err
}

func (x *Xslate) RenderString(template string, vars Vars) (string, error) {
  bc, err := x.Loader.LoadString(template)
  x.Vm.Run(bc, vm.Vars(vars))
  str, err := x.Vm.OutputString()
  return str, err
}
<commit_msg>slight code optimization<commit_after>/*
xslate is an extremely powerful template engine, based on Perl5's Text::Xslate
module. Xslate uses a virtual machine to execute pre-compiled template bytecode,
which gives its flexibility while maitaining a very fast execution speed.

Note that RenderString() DOES NOT CACHE THE GENERATED BYTECODE. This has
significant effect on performance if you repeatedly call the same template

*/
package xslate

import (
  "errors"
  "fmt"
  "io/ioutil"
  "os"
  "reflect"

  "github.com/lestrrat/go-xslate/compiler"
  "github.com/lestrrat/go-xslate/loader"
  "github.com/lestrrat/go-xslate/parser"
  "github.com/lestrrat/go-xslate/parser/tterse"
  "github.com/lestrrat/go-xslate/vm"
)

type Vars vm.Vars
type Xslate struct {
  Flags    int32
  Vm       *vm.VM
  Compiler compiler.Compiler
  Parser   parser.Parser
  Loader   loader.ByteCodeLoader
  // XXX Need to make syntax pluggable
}

type ConfigureArgs interface {
  Get(string) (interface {}, bool)
}

type Args map[string]interface {}

func DefaultCompiler(tx *Xslate, args Args) error {
  tx.Compiler = compiler.New()
  return nil
}

func DefaultParser(tx *Xslate, args Args) error {
  tx.Parser = tterse.New()
  return nil
}

func DefaultLoader(tx *Xslate, args Args) error {
  var tmp interface {}
  tmp, ok := args.Get("CacheDir")
  if !ok {
    tmp, _ = ioutil.TempDir("", "go-xslate-cache-")
  }
  cacheDir := tmp.(string)

  tmp, ok = args.Get("LoadPaths")
  if !ok {
    cwd, _ := os.Getwd()
    tmp = []string { cwd }
  }
  paths := tmp.([]string)

  cache, err := loader.NewFileCache(cacheDir)
  if err != nil {
    return err
  }
  fileloader, err := loader.NewFileTemplateLoader(paths)
  if err != nil {
    return err
  }
  tx.Loader = loader.NewCachedByteCodeLoader(cache, fileloader, tx.Parser, tx.Compiler)
  return nil
}

func DefaultVm(tx *Xslate, args Args) error {
  tx.Vm = vm.NewVM()
  tx.Vm.Loader = tx.Loader
  return nil
}

func (args Args) Get(key string) (interface {}, bool) {
  ret, ok := args[key]
  return ret, ok
}

func (tx *Xslate) configureGeneric(configuror interface {}, args Args) error {
  ref := reflect.ValueOf(configuror)
  switch ref.Type().Kind() {
  case reflect.Func:
    // If this is a function, it better take our Xslate instance as the
    // sole argument, and initialize it as it pleases
    if ref.Type().NumIn() != 2 && (ref.Type().In(0).Name() != "Xslate" || ref.Type().In(1).Name() != "Args") {
      panic(fmt.Sprintf(`Expected function initializer "func (tx *Xslate ", but instead of %s`, ref.Type))
    }
    cb := configuror.(func(*Xslate, Args) error)
    err := cb(tx, args)
    return err
  }
  return errors.New("Bad configurator")
}

func (tx *Xslate) Configure(args ConfigureArgs) error {
  // The compiler currently does not have any configurable options, but
  // one may want to replace the entire compiler struct
  defaults := map[string]func(*Xslate, Args) error {
    "Compiler": DefaultCompiler,
    "Parser":   DefaultParser,
    "Loader":   DefaultLoader,
    "Vm":       DefaultVm,
  }

  for _, key := range []string { "Parser", "Compiler", "Loader", "Vm" } {
    configKey := "Configure" + key
    configuror, ok := args.Get(configKey);
    if !ok {
      configuror = defaults[key]
    }

    args, ok := args.Get(key)
    if !ok {
      args = Args {}
    }

    err := tx.configureGeneric(configuror, args.(Args))
    if err != nil {
      return err
    }
  }

  return nil
}

func New(args ...Args) (*Xslate, error) {
  tx := &Xslate {}

  // We jump through hoops because there are A LOT of configuration options
  // but most of them only need to use the default values
  if len(args) <= 0 {
    args = []Args { Args {} }
  }
  err := tx.Configure(args[0])
  if err != nil {
    return nil, err
  }
  return tx, nil
}

func (tx *Xslate) DumpAST(b bool) {
  tx.Loader.DumpAST(b)
}

func (tx *Xslate) DumpByteCode(b bool) {
  tx.Loader.DumpByteCode(b)
}

func (x *Xslate) Render(name string, vars Vars) (string, error) {
  bc, err := x.Loader.Load(name)
  if err != nil {
    return "", err
  }
  x.Vm.Run(bc, vm.Vars(vars))
  return x.Vm.OutputString()
}

func (x *Xslate) RenderString(template string, vars Vars) (string, error) {
  bc, err := x.Loader.LoadString(template)
  if err != nil {
    return "", err
  }

  x.Vm.Run(bc, vm.Vars(vars))
  return x.Vm.OutputString()
}
<|endoftext|>


<commit_before>package wait

import "time"

func New(d time.Duration) (waiter <-chan time.Time, done func()) {
	wait := make(chan time.Time)
	stop := make(chan struct{})
	go func() {
		for {
			select {
			case wait <- time.Now():
				time.Sleep(d)
			case <-stop:
				return
			}
		}
	}()
	return wait, func() {
		close(stop)
	}
}
<commit_msg>Remove dead code<commit_after><|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
)

var addr = flag.String("addr", ":4089", "Server address")
var timeout = flag.Duration("timeout", 5*time.Second, "Timeout for HTTP GETs")
var conditionsInterval = flag.Duration("conditions_interval", 10*time.Minute, "Interval between updating conditions")
var siteMapUrl = flag.String("site_map_url", "http://magicseaweed.com/site-map.php", "URL of the site map")
var conditionsFilePath = flag.String("conditions_file", "conditions.json", "Path to file with conditions cache")
var conditionsPeriod = flag.Duration("conditions_period", 10*time.Second, "How often to save the conditions file")
var help = flag.Bool("h", false, "Show help message")

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		os.Exit(0)
	}
	loadConditionsFile()
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/errors", handleErrors)
	go keepConditionsUpdated()
	log.Printf("Listening on %s", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

var head = template.HTML(`

		<title>Waveguide</title>

`)

var errsTmpl = template.Must(template.New("errs").Parse(`

{{.Head}}

		Errors:

			{{range .Errs}}

				{{.Loc.HTMLName}}
				{{.Err}}

			{{end}}

`))

var tmpl = template.Must(template.New("main").Parse(`

{{.Head}}

		{{if .Conds}}

				Location
				Conditions
				Wave Height

				{{range .Conds}}

					{{.Loc.HTMLName}}
					{{.Stars}}
					{{.Details}}

				{{end}}

		{{end}}

`))

type Location struct {
	Name             string
	MagicSeaweedPath string
}

func (loc *Location) HTMLName() template.HTML {
	return template.HTML(loc.Name)
}

type Conditions struct {
	Loc     *Location
	Rating  int
	Details string
}

type Error struct {
	Loc *Location
	Err error
}

func handleErrors(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	defer mu.Unlock()
	data := struct {
		Errs []*Error
		Head template.HTML
	}{
		Errs: errs,
		Head: head,
	}
	err := errsTmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	// Sort conditions by rating and name.
	mu.Lock()
	defer mu.Unlock()
	conds2 := make([]*Conditions, 0, len(conds))
	for _, c := range conds {
		conds2 = append(conds2, c)
	}
	sort.Sort(ByRating(conds2))

	// Render the results.
	data := struct {
		Conds []*Conditions
		Head  template.HTML
	}{Conds: conds2, Head: head}
	err := tmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func updateConditionsAllLocations() {
	client := &http.Client{Timeout: *timeout}

	// Gather locations.
	log.Printf("Fetching %s", *siteMapUrl)
	resp, err := client.Get(*siteMapUrl)
	if err != nil {
		log.Printf("Failed to get site map. %v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read response body of site map. %v", err)
	}
	reportMatches := reportRx.FindAll(body, -1)
	for _, match := range reportMatches {
		path := string(match)
		name, err := surfReportPathToName(path)
		if err != nil {
			log.Printf("Failed to convert surf report path to name. %v", err)
			continue
		}
		_, ok := locations[name]
		if !ok {
			log.Printf("Got new location: %s: %s", name, path)
		}
		loc := &Location{
			Name:             name,
			MagicSeaweedPath: path,
		}
		locations[name] = loc
	}
	log.Printf("Found %d reports", len(reportMatches))
	log.Printf("New number of locations: %d", len(locations))

	// Gather conditions and errors.
	mu.Lock()
	errs = make([]*Error, 0, len(locations))
	mu.Unlock()
	for _, loc := range locations {
		cond, err := loc.GetConditions(client)
		if err != nil {
			mu.Lock()
			e := &Error{
				Loc: loc,
				Err: err,
			}
			errs = append(errs, e)
			mu.Unlock()
			continue
		}
		mu.Lock()
		conds[loc.Name] = cond
		mu.Unlock()
		saveConditionsFile()
	}
}

var srpTailRx = regexp.MustCompile(`-Surf-Report/\d+/`)

func surfReportPathToName(srp string) (string, error) {
	s := srpTailRx.ReplaceAllString(srp, "")
	s, err := url.PathUnescape(s)
	if err != nil {
		return "", fmt.Errorf("Failed to unescape path in surfReportPathToName(%q). %v", srp, err)
	}
	s = html.UnescapeString(s)
	s = s[1:] // Remove leading /
	s = strings.Replace(s, "-", " ", -1)
	return s, nil
}

func loadConditionsFile() {
	f, err := os.Open(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s. %v. That's okay.", *conditionsFilePath, err)
		return
	}
	contents, err := ioutil.ReadAll(f)
	err = json.Unmarshal(contents, &conds)
	if err != nil {
		log.Printf("Failed to unmarshall the conditions map. %v", err)
		os.Exit(1)
	}
	log.Printf("Loaded conditions file %s", *conditionsFilePath)
}

func saveConditionsFile() {
	f, err := os.Create(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s for writing. %v", *conditionsFilePath)
		return
	}
	mu.Lock()
	defer mu.Unlock()
	contents, err := json.Marshal(conds)
	if err != nil {
		log.Printf("Failed to marshal conditions. %v", conds)
		return
	}
	f.Write(contents)
	f.Close()
	log.Printf("Saved conditions file %s", *conditionsFilePath)
}

var locations = make(map[string]*Location)
var mu sync.Mutex // for conds and errs
var conds = make(map[string]*Conditions)
var errs []*Error

type ByRating []*Conditions

func (r ByRating) Len() int      { return len(r) }
func (r ByRating) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByRating) Less(i, j int) bool {
	ci := r[i]
	cj := r[j]
	if ci.Rating == cj.Rating {
		return ci.Loc.Name < cj.Loc.Name
	}
	return ci.Rating > cj.Rating
}

var starSectionRx = regexp.MustCompile(`<ul class="rating rating-large clearfix">.*?</ul>`)
var starRx = regexp.MustCompile(`<li class="active"> *<i class="glyphicon glyphicon-star"></i> *</li>`)
var heightRx = regexp.MustCompile(`(\d+(?:-\d+)?)<small>ft`)
var reportRx = regexp.MustCompile(`/[^"/]+-Surf-Report/\d+/`)

func (loc *Location) GetConditions(client *http.Client) (*Conditions, error) {
	url := "http://magicseaweed.com" + loc.MagicSeaweedPath
	log.Printf("Fetching %s", url)
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body. %v", err)
	}
	rating := countTopStars(body)
	hMatch := heightRx.FindSubmatch(body)
	if len(hMatch) != 2 {
		return nil, fmt.Errorf("Wave height regex failed.")
	}
	details := fmt.Sprintf("%s ft", hMatch[1])
	cond := &Conditions{
		Loc:     loc,
		Rating:  rating,
		Details: details,
	}
	return cond, nil
}

// countTopStars returns the number of stars in the first rating section on the page.
func countTopStars(body []byte) int {
	starSection := starSectionRx.Find(body)
	foundStars := starRx.FindAll(starSection, -1)
	return len(foundStars)
}

func (c *Conditions) Stars() string {
	runes := make([]rune, 0, 5)
	for i := 0; i < c.Rating; i++ {
		runes = append(runes, '★')
	}
	for i := 0; i < 5-c.Rating; i++ {
		runes = append(runes, '☆')
	}
	return string(runes)
}

func keepConditionsUpdated() {
	updateConditionsAllLocations()
	tick := time.Tick(*conditionsInterval)
	for {
		<-tick
		updateConditionsAllLocations()
	}
}
<commit_msg>Move some code around.<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"sort"
	"strings"
	"sync"
	"time"
)

var addr = flag.String("addr", ":4089", "Server address")
var timeout = flag.Duration("timeout", 5*time.Second, "Timeout for HTTP GETs")
var conditionsInterval = flag.Duration("conditions_interval", 10*time.Minute, "Interval between updating conditions")
var siteMapUrl = flag.String("site_map_url", "http://magicseaweed.com/site-map.php", "URL of the site map")
var conditionsFilePath = flag.String("conditions_file", "conditions.json", "Path to file with conditions cache")
var conditionsPeriod = flag.Duration("conditions_period", 10*time.Second, "How often to save the conditions file")
var help = flag.Bool("h", false, "Show help message")

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		os.Exit(0)
	}
	loadConditionsFile()
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/errors", handleErrors)
	go keepConditionsUpdated()
	log.Printf("Listening on %s", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

var head = template.HTML(`

		<title>Waveguide</title>

`)

var errsTmpl = template.Must(template.New("errs").Parse(`

{{.Head}}

		Errors:

			{{range .Errs}}

				{{.Loc.HTMLName}}
				{{.Err}}

			{{end}}

`))

var tmpl = template.Must(template.New("main").Parse(`

{{.Head}}

		{{if .Conds}}

				Location
				Conditions
				Wave Height

				{{range .Conds}}

					{{.Loc.HTMLName}}
					{{.Stars}}
					{{.Details}}

				{{end}}

		{{end}}

`))

type Location struct {
	Name             string
	MagicSeaweedPath string
}

func (loc *Location) HTMLName() template.HTML {
	return template.HTML(loc.Name)
}

type Conditions struct {
	Loc     *Location
	Rating  int
	Details string
}

type Error struct {
	Loc *Location
	Err error
}

func handleErrors(w http.ResponseWriter, r *http.Request) {
	mu.Lock()
	defer mu.Unlock()
	data := struct {
		Errs []*Error
		Head template.HTML
	}{
		Errs: errs,
		Head: head,
	}
	err := errsTmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func handleRoot(w http.ResponseWriter, r *http.Request) {
	// Sort conditions by rating and name.
	mu.Lock()
	defer mu.Unlock()
	conds2 := make([]*Conditions, 0, len(conds))
	for _, c := range conds {
		conds2 = append(conds2, c)
	}
	sort.Sort(ByRating(conds2))

	// Render the results.
	data := struct {
		Conds []*Conditions
		Head  template.HTML
	}{Conds: conds2, Head: head}
	err := tmpl.Execute(w, data)
	if err != nil {
		log.Printf("Failed to execute template. %v", err)
	}
}

func updateConditionsAllLocations() {
	client := &http.Client{Timeout: *timeout}

	// Gather locations.
	log.Printf("Fetching %s", *siteMapUrl)
	resp, err := client.Get(*siteMapUrl)
	if err != nil {
		log.Printf("Failed to get site map. %v", err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Failed to read response body of site map. %v", err)
	}
	reportMatches := reportRx.FindAll(body, -1)
	for _, match := range reportMatches {
		path := string(match)
		name, err := surfReportPathToName(path)
		if err != nil {
			log.Printf("Failed to convert surf report path to name. %v", err)
			continue
		}
		_, ok := locations[name]
		if !ok {
			log.Printf("Got new location: %s: %s", name, path)
		}
		loc := &Location{
			Name:             name,
			MagicSeaweedPath: path,
		}
		locations[name] = loc
	}
	log.Printf("Found %d reports", len(reportMatches))
	log.Printf("New number of locations: %d", len(locations))

	// Gather conditions and errors.
	mu.Lock()
	errs = make([]*Error, 0, len(locations))
	mu.Unlock()
	for _, loc := range locations {
		cond, err := loc.GetConditions(client)
		if err != nil {
			mu.Lock()
			e := &Error{
				Loc: loc,
				Err: err,
			}
			errs = append(errs, e)
			mu.Unlock()
			continue
		}
		mu.Lock()
		conds[loc.Name] = cond
		mu.Unlock()
		saveConditionsFile()
	}
}

func loadConditionsFile() {
	f, err := os.Open(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s. %v. That's okay.", *conditionsFilePath, err)
		return
	}
	contents, err := ioutil.ReadAll(f)
	err = json.Unmarshal(contents, &conds)
	if err != nil {
		log.Printf("Failed to unmarshall the conditions map. %v", err)
		os.Exit(1)
	}
	log.Printf("Loaded conditions file %s", *conditionsFilePath)
}

func saveConditionsFile() {
	f, err := os.Create(*conditionsFilePath)
	if err != nil {
		log.Printf("Failed to open conditions file %s for writing. %v", *conditionsFilePath)
		return
	}
	mu.Lock()
	defer mu.Unlock()
	contents, err := json.Marshal(conds)
	if err != nil {
		log.Printf("Failed to marshal conditions. %v", conds)
		return
	}
	f.Write(contents)
	f.Close()
	log.Printf("Saved conditions file %s", *conditionsFilePath)
}

var locations = make(map[string]*Location)
var mu sync.Mutex // for conds and errs
var conds = make(map[string]*Conditions)
var errs []*Error

type ByRating []*Conditions

func (r ByRating) Len() int      { return len(r) }
func (r ByRating) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r ByRating) Less(i, j int) bool {
	ci := r[i]
	cj := r[j]
	if ci.Rating == cj.Rating {
		return ci.Loc.Name < cj.Loc.Name
	}
	return ci.Rating > cj.Rating
}

var starSectionRx = regexp.MustCompile(`<ul class="rating rating-large clearfix">.*?</ul>`)
var starRx = regexp.MustCompile(`<li class="active"> *<i class="glyphicon glyphicon-star"></i> *</li>`)
var heightRx = regexp.MustCompile(`(\d+(?:-\d+)?)<small>ft`)
var reportRx = regexp.MustCompile(`/[^"/]+-Surf-Report/\d+/`)
var srpTailRx = regexp.MustCompile(`-Surf-Report/\d+/`)

func surfReportPathToName(srp string) (string, error) {
	s := srpTailRx.ReplaceAllString(srp, "")
	s, err := url.PathUnescape(s)
	if err != nil {
		return "", fmt.Errorf("Failed to unescape path in surfReportPathToName(%q). %v", srp, err)
	}
	s = html.UnescapeString(s)
	s = s[1:] // Remove leading /
	s = strings.Replace(s, "-", " ", -1)
	return s, nil
}

func (loc *Location) GetConditions(client *http.Client) (*Conditions, error) {
	url := "http://magicseaweed.com" + loc.MagicSeaweedPath
	log.Printf("Fetching %s", url)
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("Failed to read body. %v", err)
	}
	rating := countTopStars(body)
	hMatch := heightRx.FindSubmatch(body)
	if len(hMatch) != 2 {
		return nil, fmt.Errorf("Wave height regex failed.")
	}
	details := fmt.Sprintf("%s ft", hMatch[1])
	cond := &Conditions{
		Loc:     loc,
		Rating:  rating,
		Details: details,
	}
	return cond, nil
}

// countTopStars returns the number of stars in the first rating section on the page.
func countTopStars(body []byte) int {
	starSection := starSectionRx.Find(body)
	foundStars := starRx.FindAll(starSection, -1)
	return len(foundStars)
}

func (c *Conditions) Stars() string {
	runes := make([]rune, 0, 5)
	for i := 0; i < c.Rating; i++ {
		runes = append(runes, '★')
	}
	for i := 0; i < 5-c.Rating; i++ {
		runes = append(runes, '☆')
	}
	return string(runes)
}

func keepConditionsUpdated() {
	updateConditionsAllLocations()
	tick := time.Tick(*conditionsInterval)
	for {
		<-tick
		updateConditionsAllLocations()
	}
}
<|endoftext|>