<commit_before>package api

import (
	graph "atlantis/manager/api/graph"
	"atlantis/manager/crypto"
	"atlantis/manager/rpc"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/cespare/go-apachelog"
	"github.com/gorilla/mux"
	"log"
	"net"
	"net/http"
	"os"
)

const (
	staticDir = "static"
)

var (
	notFoundHTML    = "404 not found"
	serverErrorHTML = "500 internal server error"
	server          *http.Server
	lAddr           = ""
	manager         = new(rpc.ManagerRPC)
	HandlerFunc     = func(h http.Handler) http.Handler {
		return h
	}
)

func NotFound(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprint(w, notFoundHTML)
}

func serverError(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprint(w, serverErrorHTML)
}

func Init(listenAddr string) error {
	gmux := mux.NewRouter() // Use gorilla mux for APIs to make things easier

	gmux.NotFoundHandler = http.HandlerFunc(NotFound)
	// APIs should go here
	gmux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, staticDir+"/img/favicon.ico")
	})
	gmux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, staticDir+"/dashboard", 302)
	})

	// Login
	gmux.HandleFunc("/login", Login).Methods("POST")

	// Task Management
	gmux.HandleFunc("/tasks/list/{Type}", ListTaskIDs).Methods("GET")
	gmux.HandleFunc("/tasks/status/{ID}", GetTaskStatus).Methods("GET")

	// Manager Management
	gmux.HandleFunc("/health", Health).Methods("GET")
	gmux.HandleFunc("/managers", ListManagers).Methods("GET")
	gmux.HandleFunc("/managers/{Region}/{Host}", GetManager).Methods("GET")
	gmux.HandleFunc("/managers/{Region}/{Host}", RegisterManager).Methods("PUT")
	gmux.HandleFunc("/managers/{Region}/{Host}", UnregisterManager).Methods("DELETE")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}", AddRole).Methods("PUT")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}", RemoveRole).Methods("DELETE")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}/{Type}", AddRoleType).Methods("PUT")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}/{Type}", RemoveRoleType).Methods("DELETE")
	gmux.HandleFunc("/managers/self", GetSelf).Methods("GET")

	// Supervisor Management
	gmux.HandleFunc("/supervisors", ListSupervisors).Methods("GET")
	gmux.HandleFunc("/supervisors/{Host}", RegisterSupervisor).Methods("PUT")
	gmux.HandleFunc("/supervisors/{Host}", UnregisterSupervisor).Methods("DELETE")

	// Router Management
	gmux.HandleFunc("/routers", ListRouters).Methods("GET")
	gmux.HandleFunc("/routers/{Zone}/{Host}", GetRouter).Methods("GET")
	gmux.HandleFunc("/routers/{Zone}/{Host}", RegisterRouter).Methods("PUT")
	gmux.HandleFunc("/routers/{Zone}/{Host}", UnregisterRouter).Methods("DELETE")

	// App Management
	gmux.HandleFunc("/apps", ListRegisteredApps).Methods("GET")
	gmux.HandleFunc("/apps/{App}", GetApp).Methods("GET")
	gmux.HandleFunc("/apps/{App}", RegisterApp).Methods("PUT")
	gmux.HandleFunc("/apps/{App}", UpdateApp).Methods("POST")
	gmux.HandleFunc("/apps/{App}", UnregisterApp).Methods("DELETE")
	gmux.HandleFunc("/apps/{App}/env/{Env}", AddDependerEnvData).Methods("PUT")
	gmux.HandleFunc("/apps/{App}/env/{Env}", GetDependerEnvData).Methods("GET")
	gmux.HandleFunc("/apps/{App}/env/{Env}", RemoveDependerEnvData).Methods("DELETE")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}", AddDependerAppData).Methods("PUT")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}", GetDependerAppData).Methods("GET")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}", RemoveDependerAppData).Methods("DELETE")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}/env/{Env}", AddDependerEnvDataForDependerApp).Methods("PUT")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}/env/{Env}", GetDependerEnvDataForDependerApp).Methods("GET")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}/env/{Env}", RemoveDependerEnvDataForDependerApp).Methods("DELETE")

	// Container Health
	gmux.HandleFunc("/healthz", ContainerHealthzGet).Methods("GET")

	// Router Config Management
	gmux.HandleFunc("/pools", ListPools).Methods("GET")
	gmux.HandleFunc("/pools/{PoolName}", GetPool).Methods("GET")
	gmux.HandleFunc("/pools/{PoolName}", UpdatePool).Methods("PUT")
	gmux.HandleFunc("/pools/{PoolName}", DeletePool).Methods("DELETE")
	gmux.HandleFunc("/rules", ListRules).Methods("GET")
	gmux.HandleFunc("/rules/{RuleName}", GetRule).Methods("GET")
	gmux.HandleFunc("/rules/{RuleName}", UpdateRule).Methods("PUT")
	gmux.HandleFunc("/rules/{RuleName}", DeleteRule).Methods("DELETE")
	gmux.HandleFunc("/tries", ListTries).Methods("GET")
	gmux.HandleFunc("/tries/{TrieName}", GetTrie).Methods("GET")
	gmux.HandleFunc("/tries/{TrieName}", UpdateTrie).Methods("PUT")
	gmux.HandleFunc("/tries/{TrieName}", DeleteTrie).Methods("DELETE")
	gmux.HandleFunc("/ports/apps/{App}/envs/{Env}", GetAppEnvPort).Methods("GET")
	gmux.HandleFunc("/ports/apps", ListAppEnvsWithPort).Methods("GET")
	gmux.HandleFunc("/ports/{Port}", GetPort).Methods("GET")
	gmux.HandleFunc("/ports/{Port}", UpdatePort).Methods("PUT")
	gmux.HandleFunc("/ports/{Port}", DeletePort).Methods("DELETE")
	gmux.HandleFunc("/ports", ListPorts).Methods("GET")

	// Router Visualizations
	gmux.HandleFunc("/visualize/router", graph.VisualizeIndex)
	gmux.HandleFunc("/visualize/router/tries/{name}/json", graph.TrieJson)
	gmux.HandleFunc("/visualize/router/tries/{name}/dot", graph.TrieDot)
	gmux.HandleFunc("/visualize/router/tries/{name}/svg", graph.TrieSvg)

	// Instance Management
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs/{Env}/containers", ListContainers).Methods("GET")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs/{Env}/containers", Deploy).Methods("POST")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs/{Env}", Teardown).Methods("DELETE")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs", DeployListEnvs).Methods("GET")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}", Teardown).Methods("DELETE")
	gmux.HandleFunc("/instances/apps/{App}/shas", ListShas).Methods("GET")
	gmux.HandleFunc("/instances/apps/{App}", Teardown).Methods("DELETE")
	gmux.HandleFunc("/instances/apps", ListApps).Methods("GET")
	gmux.HandleFunc("/instances/{ID}/copyOrphaned", CopyOrphaned).Methods("POST")
	gmux.HandleFunc("/instances/{ID}/copy", CopyContainer).Methods("POST")
	gmux.HandleFunc("/instances/{ID}/move", MoveContainer).Methods("POST")
	gmux.HandleFunc("/instances/{ID}/maint", ContainerMaintenance).Methods("POST")
	gmux.HandleFunc("/instances/{ID}", ContainerIDGet).Methods("GET")
	gmux.HandleFunc("/instances/{ID}", TeardownContainerID).Methods("DELETE")
	gmux.HandleFunc("/instances", ListContainers).Methods("GET")
	gmux.HandleFunc("/instances", TeardownContainers).Methods("DELETE")

	// LDAP Management
	gmux.HandleFunc("/users/{User}", GetPermissions).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/apps", ListTeamApps).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/apps/{App}", AllowApp).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/apps/{App}", DisallowApp).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}/admins", ListTeamAdmins).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/admins/{Admin}", AddTeamAdmin).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/admins/{Admin}", RemoveTeamAdmin).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}/emails/{Email}", AddTeamEmail).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/emails/{Email}", RemoveTeamEmail).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}/members", ListTeamMembers).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/members/{Member}", AddTeamMember).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/members/{Member}", RemoveTeamMember).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}", CreateTeam).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}", DeleteTeam).Methods("DELETE")
	gmux.HandleFunc("/teams", ListTeams).Methods("GET")

	// Environment Management
	gmux.HandleFunc("/envs/{Env}/app/{App}/resolve/{DepNames}", ResolveDeps).Methods("GET")
	gmux.HandleFunc("/envs/{Env}", UpdateEnv).Methods("PUT")
	gmux.HandleFunc("/envs/{Env}", DeleteEnv).Methods("DELETE")
	gmux.HandleFunc("/envs", ListEnvs).Methods("GET")

	// Static Assets
	staticPath := "/" + staticDir + "/"
	fileServer := http.StripPrefix(staticPath, http.FileServer(http.Dir("./"+staticDir)))
	gmux.NewRoute().PathPrefix(staticPath).Handler(fileServer)

	handler := apachelog.NewHandler(HandlerFunc(gmux), os.Stderr)
	server = &http.Server{Addr: listenAddr, Handler: handler}
	lAddr = listenAddr
	return nil
}

func listenAndServeTLS(cert, key []byte) error {
	addr := server.Addr
	if addr == "" {
		log.Printf("Current Address: %s", addr)
		panic("[API] Current Address is not HTTPS")
	}

	config := &tls.Config{}
	if server.TLSConfig != nil {
		config = server.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.X509KeyPair(cert, key)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", lAddr)
	if err != nil {
		return err
	}

	return server.Serve(tls.NewListener(conn, config))
}

func Output(obj map[string]interface{}, err error) string {
	var bytes []byte
	if err != nil {
		m := map[string]interface{}{"Error": err.Error()}
		bytes, err = json.Marshal(m)
		if err != nil {
			return err.Error()
		}
		return string(bytes)
	}
	bytes, err = json.Marshal(obj)
	return string(bytes)
}

func Listen() {
	if server == nil {
		panic("Not Initialized.")
	}
	log.Println("[API] Listening on", lAddr)
	log.Fatal(listenAndServeTLS(crypto.SERVER_CERT, crypto.SERVER_KEY).Error())
}
<commit_msg>redirect to #dashboard directly<commit_after>package api

import (
	graph "atlantis/manager/api/graph"
	"atlantis/manager/crypto"
	"atlantis/manager/rpc"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/cespare/go-apachelog"
	"github.com/gorilla/mux"
	"log"
	"net"
	"net/http"
	"os"
)

const (
	staticDir = "static"
)

var (
	notFoundHTML    = "404 not found"
	serverErrorHTML = "500 internal server error"
	server          *http.Server
	lAddr           = ""
	manager         = new(rpc.ManagerRPC)
	HandlerFunc     = func(h http.Handler) http.Handler {
		return h
	}
)

func NotFound(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusNotFound)
	fmt.Fprint(w, notFoundHTML)
}

func serverError(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(http.StatusInternalServerError)
	fmt.Fprint(w, serverErrorHTML)
}

func Init(listenAddr string) error {
	gmux := mux.NewRouter() // Use gorilla mux for APIs to make things easier

	gmux.NotFoundHandler = http.HandlerFunc(NotFound)
	// APIs should go here
	gmux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, staticDir+"/img/favicon.ico")
	})
	gmux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, staticDir+"/dashboard/#dashboard", 302)
	})

	// Login
	gmux.HandleFunc("/login", Login).Methods("POST")

	// Task Management
	gmux.HandleFunc("/tasks/list/{Type}", ListTaskIDs).Methods("GET")
	gmux.HandleFunc("/tasks/status/{ID}", GetTaskStatus).Methods("GET")

	// Manager Management
	gmux.HandleFunc("/health", Health).Methods("GET")
	gmux.HandleFunc("/managers", ListManagers).Methods("GET")
	gmux.HandleFunc("/managers/{Region}/{Host}", GetManager).Methods("GET")
	gmux.HandleFunc("/managers/{Region}/{Host}", RegisterManager).Methods("PUT")
	gmux.HandleFunc("/managers/{Region}/{Host}", UnregisterManager).Methods("DELETE")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}", AddRole).Methods("PUT")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}", RemoveRole).Methods("DELETE")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}/{Type}", AddRoleType).Methods("PUT")
	gmux.HandleFunc("/managers/{Region}/{Host}/roles/{Role}/{Type}", RemoveRoleType).Methods("DELETE")
	gmux.HandleFunc("/managers/self", GetSelf).Methods("GET")

	// Supervisor Management
	gmux.HandleFunc("/supervisors", ListSupervisors).Methods("GET")
	gmux.HandleFunc("/supervisors/{Host}", RegisterSupervisor).Methods("PUT")
	gmux.HandleFunc("/supervisors/{Host}", UnregisterSupervisor).Methods("DELETE")

	// Router Management
	gmux.HandleFunc("/routers", ListRouters).Methods("GET")
	gmux.HandleFunc("/routers/{Zone}/{Host}", GetRouter).Methods("GET")
	gmux.HandleFunc("/routers/{Zone}/{Host}", RegisterRouter).Methods("PUT")
	gmux.HandleFunc("/routers/{Zone}/{Host}", UnregisterRouter).Methods("DELETE")

	// App Management
	gmux.HandleFunc("/apps", ListRegisteredApps).Methods("GET")
	gmux.HandleFunc("/apps/{App}", GetApp).Methods("GET")
	gmux.HandleFunc("/apps/{App}", RegisterApp).Methods("PUT")
	gmux.HandleFunc("/apps/{App}", UpdateApp).Methods("POST")
	gmux.HandleFunc("/apps/{App}", UnregisterApp).Methods("DELETE")
	gmux.HandleFunc("/apps/{App}/env/{Env}", AddDependerEnvData).Methods("PUT")
	gmux.HandleFunc("/apps/{App}/env/{Env}", GetDependerEnvData).Methods("GET")
	gmux.HandleFunc("/apps/{App}/env/{Env}", RemoveDependerEnvData).Methods("DELETE")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}", AddDependerAppData).Methods("PUT")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}", GetDependerAppData).Methods("GET")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}", RemoveDependerAppData).Methods("DELETE")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}/env/{Env}", AddDependerEnvDataForDependerApp).Methods("PUT")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}/env/{Env}", GetDependerEnvDataForDependerApp).Methods("GET")
	gmux.HandleFunc("/apps/{App}/depender/{Depender}/env/{Env}", RemoveDependerEnvDataForDependerApp).Methods("DELETE")

	// Container Health
	gmux.HandleFunc("/healthz", ContainerHealthzGet).Methods("GET")

	// Router Config Management
	gmux.HandleFunc("/pools", ListPools).Methods("GET")
	gmux.HandleFunc("/pools/{PoolName}", GetPool).Methods("GET")
	gmux.HandleFunc("/pools/{PoolName}", UpdatePool).Methods("PUT")
	gmux.HandleFunc("/pools/{PoolName}", DeletePool).Methods("DELETE")
	gmux.HandleFunc("/rules", ListRules).Methods("GET")
	gmux.HandleFunc("/rules/{RuleName}", GetRule).Methods("GET")
	gmux.HandleFunc("/rules/{RuleName}", UpdateRule).Methods("PUT")
	gmux.HandleFunc("/rules/{RuleName}", DeleteRule).Methods("DELETE")
	gmux.HandleFunc("/tries", ListTries).Methods("GET")
	gmux.HandleFunc("/tries/{TrieName}", GetTrie).Methods("GET")
	gmux.HandleFunc("/tries/{TrieName}", UpdateTrie).Methods("PUT")
	gmux.HandleFunc("/tries/{TrieName}", DeleteTrie).Methods("DELETE")
	gmux.HandleFunc("/ports/apps/{App}/envs/{Env}", GetAppEnvPort).Methods("GET")
	gmux.HandleFunc("/ports/apps", ListAppEnvsWithPort).Methods("GET")
	gmux.HandleFunc("/ports/{Port}", GetPort).Methods("GET")
	gmux.HandleFunc("/ports/{Port}", UpdatePort).Methods("PUT")
	gmux.HandleFunc("/ports/{Port}", DeletePort).Methods("DELETE")
	gmux.HandleFunc("/ports", ListPorts).Methods("GET")

	// Router Visualizations
	gmux.HandleFunc("/visualize/router", graph.VisualizeIndex)
	gmux.HandleFunc("/visualize/router/tries/{name}/json", graph.TrieJson)
	gmux.HandleFunc("/visualize/router/tries/{name}/dot", graph.TrieDot)
	gmux.HandleFunc("/visualize/router/tries/{name}/svg", graph.TrieSvg)

	// Instance Management
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs/{Env}/containers", ListContainers).Methods("GET")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs/{Env}/containers", Deploy).Methods("POST")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs/{Env}", Teardown).Methods("DELETE")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}/envs", DeployListEnvs).Methods("GET")
	gmux.HandleFunc("/instances/apps/{App}/shas/{Sha}", Teardown).Methods("DELETE")
	gmux.HandleFunc("/instances/apps/{App}/shas", ListShas).Methods("GET")
	gmux.HandleFunc("/instances/apps/{App}", Teardown).Methods("DELETE")
	gmux.HandleFunc("/instances/apps", ListApps).Methods("GET")
	gmux.HandleFunc("/instances/{ID}/copyOrphaned", CopyOrphaned).Methods("POST")
	gmux.HandleFunc("/instances/{ID}/copy", CopyContainer).Methods("POST")
	gmux.HandleFunc("/instances/{ID}/move", MoveContainer).Methods("POST")
	gmux.HandleFunc("/instances/{ID}/maint", ContainerMaintenance).Methods("POST")
	gmux.HandleFunc("/instances/{ID}", ContainerIDGet).Methods("GET")
	gmux.HandleFunc("/instances/{ID}", TeardownContainerID).Methods("DELETE")
	gmux.HandleFunc("/instances", ListContainers).Methods("GET")
	gmux.HandleFunc("/instances", TeardownContainers).Methods("DELETE")

	// LDAP Management
	gmux.HandleFunc("/users/{User}", GetPermissions).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/apps", ListTeamApps).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/apps/{App}", AllowApp).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/apps/{App}", DisallowApp).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}/admins", ListTeamAdmins).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/admins/{Admin}", AddTeamAdmin).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/admins/{Admin}", RemoveTeamAdmin).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}/emails/{Email}", AddTeamEmail).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/emails/{Email}", RemoveTeamEmail).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}/members", ListTeamMembers).Methods("GET")
	gmux.HandleFunc("/teams/{Team}/members/{Member}", AddTeamMember).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}/members/{Member}", RemoveTeamMember).Methods("DELETE")
	gmux.HandleFunc("/teams/{Team}", CreateTeam).Methods("PUT")
	gmux.HandleFunc("/teams/{Team}", DeleteTeam).Methods("DELETE")
	gmux.HandleFunc("/teams", ListTeams).Methods("GET")

	// Environment Management
	gmux.HandleFunc("/envs/{Env}/app/{App}/resolve/{DepNames}", ResolveDeps).Methods("GET")
	gmux.HandleFunc("/envs/{Env}", UpdateEnv).Methods("PUT")
	gmux.HandleFunc("/envs/{Env}", DeleteEnv).Methods("DELETE")
	gmux.HandleFunc("/envs", ListEnvs).Methods("GET")

	// Static Assets
	staticPath := "/" + staticDir + "/"
	fileServer := http.StripPrefix(staticPath, http.FileServer(http.Dir("./"+staticDir)))
	gmux.NewRoute().PathPrefix(staticPath).Handler(fileServer)

	handler := apachelog.NewHandler(HandlerFunc(gmux), os.Stderr)
	server = &http.Server{Addr: listenAddr, Handler: handler}
	lAddr = listenAddr
	return nil
}

func listenAndServeTLS(cert, key []byte) error {
	addr := server.Addr
	if addr == "" {
		log.Printf("Current Address: %s", addr)
		panic("[API] Current Address is not HTTPS")
	}

	config := &tls.Config{}
	if server.TLSConfig != nil {
		config = server.TLSConfig
	}
	if config.NextProtos == nil {
		config.NextProtos = []string{"http/1.1"}
	}

	var err error
	config.Certificates = make([]tls.Certificate, 1)
	config.Certificates[0], err = tls.X509KeyPair(cert, key)
	if err != nil {
		return err
	}

	conn, err := net.Listen("tcp", lAddr)
	if err != nil {
		return err
	}

	return server.Serve(tls.NewListener(conn, config))
}

func Output(obj map[string]interface{}, err error) string {
	var bytes []byte
	if err != nil {
		m := map[string]interface{}{"Error": err.Error()}
		bytes, err = json.Marshal(m)
		if err != nil {
			return err.Error()
		}
		return string(bytes)
	}
	bytes, err = json.Marshal(obj)
	return string(bytes)
}

func Listen() {
	if server == nil {
		panic("Not Initialized.")
	}
	log.Println("[API] Listening on", lAddr)
	log.Fatal(listenAndServeTLS(crypto.SERVER_CERT, crypto.SERVER_KEY).Error())
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * 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 ofthe 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 specificlanguage governing permissions and
 * limitations under the License.
 *
 */

package endpoints

import (
	"fmt"
	"net/http"
	"strings"
	"sync"

	"github.com/safchain/insanelock"

	"github.com/skydive-project/skydive/graffiti/graph"
	"github.com/skydive-project/skydive/graffiti/graph/traversal"
	"github.com/skydive-project/skydive/graffiti/logging"
	"github.com/skydive-project/skydive/graffiti/messages"
	ws "github.com/skydive-project/skydive/graffiti/websocket"
)

// UpdatePolicy specifies whether we should send partial or full updates
type UpdatePolicy = string

// Update policies
const (
	FullUpdates    UpdatePolicy = "full"
	PartialUpdates              = "partial"
)

type subscriber struct {
	ws.Speaker
	lastGraph     *graph.Graph
	gremlinFilter string
	ts            *traversal.GremlinTraversalSequence
	updatePolicy  UpdatePolicy
}

func (s *subscriber) getSubGraph(g *graph.Graph, lockGraph bool) (*graph.Graph, error) {
	res, err := s.ts.Exec(g, lockGraph)
	if err != nil {
		return nil, err
	}

	tv, ok := res.(*traversal.GraphTraversal)
	if !ok {
		return nil, fmt.Errorf("Gremlin query '%s' did not return a graph", s.gremlinFilter)
	}

	return tv.Graph, nil
}

func (s *subscriber) resetFilter() {
	s.gremlinFilter = ""
	s.ts = nil
	s.lastGraph = nil
}

// SubscriberEndpoint sends all the modifications to its subscribers.
type SubscriberEndpoint struct {
	insanelock.RWMutex
	ws.DefaultSpeakerEventHandler
	pool          ws.StructSpeakerPool
	graph         *graph.Graph
	wg            sync.WaitGroup
	gremlinParser *traversal.GremlinTraversalParser
	subscribers   map[ws.Speaker]*subscriber
	logger        logging.Logger
}

func (t *SubscriberEndpoint) newSubscriber(speaker ws.Speaker, gremlinFilter string, lockGraph bool) (s *subscriber, err error) {
	s = &subscriber{Speaker: speaker, gremlinFilter: gremlinFilter}

	if gremlinFilter != "" {
		s.ts, err = t.gremlinParser.Parse(strings.NewReader(gremlinFilter))
		if err != nil {
			return nil, fmt.Errorf("Invalid Gremlin filter '%s' for client %s", gremlinFilter, speaker.GetRemoteHost())
		}

		if s.lastGraph, err = s.getSubGraph(t.graph, lockGraph); err != nil {
			return nil, err
		}
	} else {
		s.lastGraph = t.graph
	}

	return s, nil
}

// OnConnected called when a subscriber got connected.
func (t *SubscriberEndpoint) OnConnected(c ws.Speaker) error {
	gremlinFilter := c.GetHeaders().Get("X-Gremlin-Filter")
	if gremlinFilter == "" {
		gremlinFilter = c.GetURL().Query().Get("x-gremlin-filter")
	}

	subscriber, err := t.newSubscriber(c, gremlinFilter, true)
	if err != nil {
		t.logger.Error(err)
		return err
	}

	updatePolicy := c.GetHeaders().Get("X-Update-Policy")
	if updatePolicy == "" {
		updatePolicy = c.GetURL().Query().Get("x-update-policy")
	}

	switch strings.ToLower(updatePolicy) {
	case FullUpdates, PartialUpdates:
	case "":
		updatePolicy = FullUpdates
	default:
		err = fmt.Errorf("invalid update policy '%s'", subscriber.updatePolicy)
		t.logger.Error(err)
		return err
	}

	subscriber.updatePolicy = updatePolicy

	t.logger.Infof("Client %s subscribed with filter '%s' and update policy '%s'", c.GetRemoteHost(), gremlinFilter, updatePolicy)
	t.Lock()
	t.subscribers[c] = subscriber
	t.Unlock()

	return nil
}

// OnDisconnected called when a subscriber got disconnected.
func (t *SubscriberEndpoint) OnDisconnected(c ws.Speaker) {
	t.Lock()
	delete(t.subscribers, c)
	t.Unlock()
}

// OnStructMessage is triggered when receiving a message from a subscriber.
// It only responds to SyncRequestMsgType messages
func (t *SubscriberEndpoint) OnStructMessage(c ws.Speaker, msg *ws.StructMessage) {
	msgType, obj, err := messages.UnmarshalMessage(msg)
	if err != nil {
		t.logger.Errorf("Graph: Unable to parse the event %v: %s", msg, err)
		return
	}

	// this kind of message usually comes from external clients like the WebUI
	if msgType == messages.SyncRequestMsgType {
		t.graph.RLock()
		defer t.graph.RUnlock()

		syncMsg, status := obj.(*messages.SyncRequestMsg), http.StatusOK
		result, err := t.graph.CloneWithContext(syncMsg.Context)
		if err != nil {
			t.logger.Errorf("unable to get a graph with context %+v: %s", syncMsg, err)
			reply := msg.Reply(nil, messages.SyncReplyMsgType, http.StatusBadRequest)
			c.SendMessage(reply)
			return
		}

		host := c.GetRemoteHost()

		if syncMsg.GremlinFilter != nil {
			// filter reset
			if *syncMsg.GremlinFilter == "" {
				t.Lock()
				t.subscribers[c].resetFilter()
				t.Unlock()
			} else {
				subscriber, err := t.newSubscriber(c, *syncMsg.GremlinFilter, false)
				if err != nil {
					t.logger.Error(err)

					reply := msg.Reply(err.Error(), messages.SyncReplyMsgType, http.StatusBadRequest)
					c.SendMessage(reply)

					return
				}

				t.logger.Infof("Client %s requested subscription with filter %s", host, *syncMsg.GremlinFilter)
				result = subscriber.lastGraph

				t.Lock()
				t.subscribers[c] = subscriber
				t.Unlock()
			}
		} else {
			t.RLock()
			subscriber := t.subscribers[c]
			t.RUnlock()

			if subscriber != nil {
				result = subscriber.lastGraph
			}
		}

		reply := msg.Reply(result, messages.SyncReplyMsgType, status)
		c.SendMessage(reply)

		return
	}
}

// notifyClients forwards local graph modification to subscribers. If a subscriber
// specified a Gremlin filter, a 'Diff' is applied between the previous graph state
// for this subscriber and the current graph state.
func (t *SubscriberEndpoint) notifyClients(typ string, i interface{}, ops []graph.PartiallyUpdatedOp) {
	t.RLock()
	subscribers := t.subscribers
	t.RUnlock()

	for _, subscriber := range subscribers {
		msg := i
		msgType := typ

		if subscriber.updatePolicy == PartialUpdates {
			switch typ {
			case messages.NodeUpdatedMsgType:
				msgType = messages.NodePartiallyUpdatedMsgType
				n := msg.(*graph.Node)
				msg = messages.PartiallyUpdatedMsg{
					ID:        n.ID,
					Revision:  n.Revision,
					UpdatedAt: n.UpdatedAt,
					Ops:       ops,
				}
			case messages.EdgeUpdatedMsgType:
				msgType = messages.EdgePartiallyUpdatedMsgType
				e := msg.(*graph.Edge)
				msg = messages.PartiallyUpdatedMsg{
					ID:        e.ID,
					Revision:  e.Revision,
					UpdatedAt: e.UpdatedAt,
					Ops:       ops,
				}
			}
		}

		if subscriber.gremlinFilter != "" {
			g, err := subscriber.getSubGraph(t.graph, false)
			if err != nil {
				t.logger.Error(err)
				continue
			}

			addedNodes, removedNodes, addedEdges, removedEdges := subscriber.lastGraph.Diff(g)

			for _, n := range addedNodes {
				subscriber.SendMessage(messages.NewStructMessage(messages.NodeAddedMsgType, n))
			}

			for _, n := range removedNodes {
				subscriber.SendMessage(messages.NewStructMessage(messages.NodeDeletedMsgType, n))
			}

			for _, e := range addedEdges {
				subscriber.SendMessage(messages.NewStructMessage(messages.EdgeAddedMsgType, e))
			}

			for _, e := range removedEdges {
				subscriber.SendMessage(messages.NewStructMessage(messages.EdgeDeletedMsgType, e))
			}

			// handle updates
			switch typ {
			case messages.NodeUpdatedMsgType, messages.NodePartiallyUpdatedMsgType:
				if g.GetNode(i.(*graph.Node).ID) != nil {
					subscriber.SendMessage(messages.NewStructMessage(msgType, msg))
				}
			case messages.EdgeUpdatedMsgType, messages.EdgePartiallyUpdatedMsgType:
				if g.GetEdge(i.(*graph.Edge).ID) != nil {
					subscriber.SendMessage(messages.NewStructMessage(msgType, msg))
				}
			}

			subscriber.lastGraph = g
		} else {
			subscriber.SendMessage(messages.NewStructMessage(msgType, msg))
		}
	}
}

// OnNodeUpdated graph node updated event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnNodeUpdated(n *graph.Node, ops []graph.PartiallyUpdatedOp) {
	t.notifyClients(messages.NodeUpdatedMsgType, n, ops)
}

// OnNodeAdded graph node added event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnNodeAdded(n *graph.Node) {
	t.notifyClients(messages.NodeAddedMsgType, n, nil)
}

// OnNodeDeleted graph node deleted event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnNodeDeleted(n *graph.Node) {
	t.notifyClients(messages.NodeDeletedMsgType, n, nil)
}

// OnEdgeUpdated graph edge updated event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnEdgeUpdated(e *graph.Edge, ops []graph.PartiallyUpdatedOp) {
	t.notifyClients(messages.EdgeUpdatedMsgType, e, nil)
}

// OnEdgeAdded graph edge added event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnEdgeAdded(e *graph.Edge) {
	t.notifyClients(messages.EdgeAddedMsgType, e, nil)
}

// OnEdgeDeleted graph edge deleted event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnEdgeDeleted(e *graph.Edge) {
	t.notifyClients(messages.EdgeDeletedMsgType, e, nil)
}

// NewSubscriberEndpoint returns a new server to be used by external subscribers,
// for instance the WebUI.
func NewSubscriberEndpoint(pool ws.StructSpeakerPool, g *graph.Graph, tr *traversal.GremlinTraversalParser, logger logging.Logger) *SubscriberEndpoint {
	if logger == nil {
		logger = logging.GetLogger()
	}

	t := &SubscriberEndpoint{
		graph:         g,
		pool:          pool,
		subscribers:   make(map[ws.Speaker]*subscriber),
		gremlinParser: tr,
		logger:        logger,
	}

	pool.AddEventHandler(t)

	// subscribe to the graph messages
	pool.AddStructMessageHandler(t, []string{messages.Namespace})

	// subscribe to the local graph event
	g.AddEventListener(t)
	return t
}
<commit_msg>graffiti: fix data race in subscribers map<commit_after>/*
 * Copyright (C) 2017 Red Hat, Inc.
 *
 * 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 ofthe 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 specificlanguage governing permissions and
 * limitations under the License.
 *
 */

package endpoints

import (
	"fmt"
	"net/http"
	"strings"
	"sync"

	"github.com/safchain/insanelock"

	"github.com/skydive-project/skydive/graffiti/graph"
	"github.com/skydive-project/skydive/graffiti/graph/traversal"
	"github.com/skydive-project/skydive/graffiti/logging"
	"github.com/skydive-project/skydive/graffiti/messages"
	ws "github.com/skydive-project/skydive/graffiti/websocket"
)

// UpdatePolicy specifies whether we should send partial or full updates
type UpdatePolicy = string

// Update policies
const (
	FullUpdates    UpdatePolicy = "full"
	PartialUpdates              = "partial"
)

type subscriber struct {
	ws.Speaker
	lastGraph     *graph.Graph
	gremlinFilter string
	ts            *traversal.GremlinTraversalSequence
	updatePolicy  UpdatePolicy
}

func (s *subscriber) getSubGraph(g *graph.Graph, lockGraph bool) (*graph.Graph, error) {
	res, err := s.ts.Exec(g, lockGraph)
	if err != nil {
		return nil, err
	}

	tv, ok := res.(*traversal.GraphTraversal)
	if !ok {
		return nil, fmt.Errorf("Gremlin query '%s' did not return a graph", s.gremlinFilter)
	}

	return tv.Graph, nil
}

func (s *subscriber) resetFilter() {
	s.gremlinFilter = ""
	s.ts = nil
	s.lastGraph = nil
}

// SubscriberEndpoint sends all the modifications to its subscribers.
type SubscriberEndpoint struct {
	insanelock.RWMutex
	ws.DefaultSpeakerEventHandler
	pool          ws.StructSpeakerPool
	graph         *graph.Graph
	wg            sync.WaitGroup
	gremlinParser *traversal.GremlinTraversalParser
	subscribers   map[ws.Speaker]*subscriber
	logger        logging.Logger
}

func (t *SubscriberEndpoint) newSubscriber(speaker ws.Speaker, gremlinFilter string, lockGraph bool) (s *subscriber, err error) {
	s = &subscriber{Speaker: speaker, gremlinFilter: gremlinFilter}

	if gremlinFilter != "" {
		s.ts, err = t.gremlinParser.Parse(strings.NewReader(gremlinFilter))
		if err != nil {
			return nil, fmt.Errorf("Invalid Gremlin filter '%s' for client %s", gremlinFilter, speaker.GetRemoteHost())
		}

		if s.lastGraph, err = s.getSubGraph(t.graph, lockGraph); err != nil {
			return nil, err
		}
	} else {
		s.lastGraph = t.graph
	}

	return s, nil
}

// OnConnected called when a subscriber got connected.
func (t *SubscriberEndpoint) OnConnected(c ws.Speaker) error {
	gremlinFilter := c.GetHeaders().Get("X-Gremlin-Filter")
	if gremlinFilter == "" {
		gremlinFilter = c.GetURL().Query().Get("x-gremlin-filter")
	}

	subscriber, err := t.newSubscriber(c, gremlinFilter, true)
	if err != nil {
		t.logger.Error(err)
		return err
	}

	updatePolicy := c.GetHeaders().Get("X-Update-Policy")
	if updatePolicy == "" {
		updatePolicy = c.GetURL().Query().Get("x-update-policy")
	}

	switch strings.ToLower(updatePolicy) {
	case FullUpdates, PartialUpdates:
	case "":
		updatePolicy = FullUpdates
	default:
		err = fmt.Errorf("invalid update policy '%s'", subscriber.updatePolicy)
		t.logger.Error(err)
		return err
	}

	subscriber.updatePolicy = updatePolicy

	t.logger.Infof("Client %s subscribed with filter '%s' and update policy '%s'", c.GetRemoteHost(), gremlinFilter, updatePolicy)
	t.Lock()
	t.subscribers[c] = subscriber
	t.Unlock()

	return nil
}

// OnDisconnected called when a subscriber got disconnected.
func (t *SubscriberEndpoint) OnDisconnected(c ws.Speaker) {
	t.Lock()
	delete(t.subscribers, c)
	t.Unlock()
}

// OnStructMessage is triggered when receiving a message from a subscriber.
// It only responds to SyncRequestMsgType messages
func (t *SubscriberEndpoint) OnStructMessage(c ws.Speaker, msg *ws.StructMessage) {
	msgType, obj, err := messages.UnmarshalMessage(msg)
	if err != nil {
		t.logger.Errorf("Graph: Unable to parse the event %v: %s", msg, err)
		return
	}

	// this kind of message usually comes from external clients like the WebUI
	if msgType == messages.SyncRequestMsgType {
		t.graph.RLock()
		defer t.graph.RUnlock()

		syncMsg, status := obj.(*messages.SyncRequestMsg), http.StatusOK
		result, err := t.graph.CloneWithContext(syncMsg.Context)
		if err != nil {
			t.logger.Errorf("unable to get a graph with context %+v: %s", syncMsg, err)
			reply := msg.Reply(nil, messages.SyncReplyMsgType, http.StatusBadRequest)
			c.SendMessage(reply)
			return
		}

		host := c.GetRemoteHost()

		if syncMsg.GremlinFilter != nil {
			// filter reset
			if *syncMsg.GremlinFilter == "" {
				t.Lock()
				t.subscribers[c].resetFilter()
				t.Unlock()
			} else {
				subscriber, err := t.newSubscriber(c, *syncMsg.GremlinFilter, false)
				if err != nil {
					t.logger.Error(err)

					reply := msg.Reply(err.Error(), messages.SyncReplyMsgType, http.StatusBadRequest)
					c.SendMessage(reply)

					return
				}

				t.logger.Infof("Client %s requested subscription with filter %s", host, *syncMsg.GremlinFilter)
				result = subscriber.lastGraph

				t.Lock()
				t.subscribers[c] = subscriber
				t.Unlock()
			}
		} else {
			t.RLock()
			subscriber := t.subscribers[c]
			t.RUnlock()

			if subscriber != nil {
				result = subscriber.lastGraph
			}
		}

		reply := msg.Reply(result, messages.SyncReplyMsgType, status)
		c.SendMessage(reply)

		return
	}
}

// notifyClients forwards local graph modification to subscribers. If a subscriber
// specified a Gremlin filter, a 'Diff' is applied between the previous graph state
// for this subscriber and the current graph state.
func (t *SubscriberEndpoint) notifyClients(typ string, i interface{}, ops []graph.PartiallyUpdatedOp) {
	var subscribers []*subscriber
	t.RLock()
	for _, subscriber := range t.subscribers {
		subscribers = append(subscribers, subscriber)
	}
	t.RUnlock()

	for _, subscriber := range subscribers {
		msg := i
		msgType := typ

		if subscriber.updatePolicy == PartialUpdates {
			switch typ {
			case messages.NodeUpdatedMsgType:
				msgType = messages.NodePartiallyUpdatedMsgType
				n := msg.(*graph.Node)
				msg = messages.PartiallyUpdatedMsg{
					ID:        n.ID,
					Revision:  n.Revision,
					UpdatedAt: n.UpdatedAt,
					Ops:       ops,
				}
			case messages.EdgeUpdatedMsgType:
				msgType = messages.EdgePartiallyUpdatedMsgType
				e := msg.(*graph.Edge)
				msg = messages.PartiallyUpdatedMsg{
					ID:        e.ID,
					Revision:  e.Revision,
					UpdatedAt: e.UpdatedAt,
					Ops:       ops,
				}
			}
		}

		if subscriber.gremlinFilter != "" {
			g, err := subscriber.getSubGraph(t.graph, false)
			if err != nil {
				t.logger.Error(err)
				continue
			}

			addedNodes, removedNodes, addedEdges, removedEdges := subscriber.lastGraph.Diff(g)

			for _, n := range addedNodes {
				subscriber.SendMessage(messages.NewStructMessage(messages.NodeAddedMsgType, n))
			}

			for _, n := range removedNodes {
				subscriber.SendMessage(messages.NewStructMessage(messages.NodeDeletedMsgType, n))
			}

			for _, e := range addedEdges {
				subscriber.SendMessage(messages.NewStructMessage(messages.EdgeAddedMsgType, e))
			}

			for _, e := range removedEdges {
				subscriber.SendMessage(messages.NewStructMessage(messages.EdgeDeletedMsgType, e))
			}

			// handle updates
			switch typ {
			case messages.NodeUpdatedMsgType, messages.NodePartiallyUpdatedMsgType:
				if g.GetNode(i.(*graph.Node).ID) != nil {
					subscriber.SendMessage(messages.NewStructMessage(msgType, msg))
				}
			case messages.EdgeUpdatedMsgType, messages.EdgePartiallyUpdatedMsgType:
				if g.GetEdge(i.(*graph.Edge).ID) != nil {
					subscriber.SendMessage(messages.NewStructMessage(msgType, msg))
				}
			}

			subscriber.lastGraph = g
		} else {
			subscriber.SendMessage(messages.NewStructMessage(msgType, msg))
		}
	}
}

// OnNodeUpdated graph node updated event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnNodeUpdated(n *graph.Node, ops []graph.PartiallyUpdatedOp) {
	t.notifyClients(messages.NodeUpdatedMsgType, n, ops)
}

// OnNodeAdded graph node added event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnNodeAdded(n *graph.Node) {
	t.notifyClients(messages.NodeAddedMsgType, n, nil)
}

// OnNodeDeleted graph node deleted event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnNodeDeleted(n *graph.Node) {
	t.notifyClients(messages.NodeDeletedMsgType, n, nil)
}

// OnEdgeUpdated graph edge updated event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnEdgeUpdated(e *graph.Edge, ops []graph.PartiallyUpdatedOp) {
	t.notifyClients(messages.EdgeUpdatedMsgType, e, nil)
}

// OnEdgeAdded graph edge added event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnEdgeAdded(e *graph.Edge) {
	t.notifyClients(messages.EdgeAddedMsgType, e, nil)
}

// OnEdgeDeleted graph edge deleted event. Implements the GraphEventListener interface.
func (t *SubscriberEndpoint) OnEdgeDeleted(e *graph.Edge) {
	t.notifyClients(messages.EdgeDeletedMsgType, e, nil)
}

// NewSubscriberEndpoint returns a new server to be used by external subscribers,
// for instance the WebUI.
func NewSubscriberEndpoint(pool ws.StructSpeakerPool, g *graph.Graph, tr *traversal.GremlinTraversalParser, logger logging.Logger) *SubscriberEndpoint {
	if logger == nil {
		logger = logging.GetLogger()
	}

	t := &SubscriberEndpoint{
		graph:         g,
		pool:          pool,
		subscribers:   make(map[ws.Speaker]*subscriber),
		gremlinParser: tr,
		logger:        logger,
	}

	pool.AddEventHandler(t)

	// subscribe to the graph messages
	pool.AddStructMessageHandler(t, []string{messages.Namespace})

	// subscribe to the local graph event
	g.AddEventListener(t)
	return t
}
<|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.

// +build ignore

// The run program is invoked via "go run" from src/run.bash or
// src/run.bat conditionally builds and runs the cmd/api tool.
//
// TODO(bradfitz): the "conditional" condition is always true.
// We should only do this if the user has the hg codereview extension
// enabled and verifies that the go.tools subrepo is checked out with
// a suitably recently version. In prep for the cmd/api rewrite.
package main

import (
	"fmt"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// goToolsVersion is the hg revision of the go.tools subrepo we need
// to build cmd/api.  This only needs to be updated whenever a go/types
// bug fix is needed by the cmd/api tool.
const goToolsVersion = "6698ca2900e2"

var goroot string

func main() {
	log.SetFlags(0)
	goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat}
	if goroot == "" {
		log.Fatal("No $GOROOT set.")
	}
	isGoDeveloper := exec.Command("hg", "pq").Run() == nil
	if !isGoDeveloper && !forceAPICheck() {
		fmt.Println("Skipping cmd/api checks; hg codereview extension not available and GO_FORCE_API_CHECK not set")
		return
	}

	gopath := prepGoPath()

	cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api")
	cmd.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("Error installing cmd/api: %v\n%s", err, out)
	}

	out, err = exec.Command("go", "tool", "api",
		"-c", file("go1", "go1.1"),
		"-next", file("next"),
		"-except", file("except")).CombinedOutput()
	if err != nil {
		log.Fatalf("Error running API checker: %v\n%s", err, out)
	}
	fmt.Print(string(out))
}

// file expands s to $GOROOT/api/s.txt.
// If there are more than 1, they're comma-separated.
func file(s ...string) string {
	if len(s) > 1 {
		return file(s[0]) + "," + file(s[1:]...)
	}
	return filepath.Join(goroot, "api", s[0]+".txt")
}

// GO_FORCE_API_CHECK is set by builders.
func forceAPICheck() bool {
	v, _ := strconv.ParseBool(os.Getenv("GO_FORCE_API_CHECK"))
	return v
}

// prepGoPath returns a GOPATH for the "go" tool to compile the API tool with.
// It tries to re-use a go.tools checkout from a previous run if possible,
// else it hg clones it.
func prepGoPath() string {
	const tempBase = "go.tools.TMP"

	// The GOPATH we'll return
	gopath := filepath.Join(os.TempDir(), "gopath-api", goToolsVersion)

	// cloneDir is where we run "hg clone".
	cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")

	// The dir we clone into. We only atomically rename it to finalDir on
	// clone success.
	tmpDir := filepath.Join(cloneDir, tempBase)

	// finalDir is where the checkout will live once it's complete.
	// If this exists already, we're done.
	finalDir := filepath.Join(cloneDir, "go.tools")

	if fi, err := os.Stat(finalDir); err == nil && fi.IsDir() {
		return gopath
	}

	if err := os.MkdirAll(cloneDir, 0700); err != nil {
		log.Fatal(err)
	}
	cmd := exec.Command("hg",
		"clone", "--rev="+goToolsVersion,
		"https://code.google.com/p/go.tools",
		tempBase)
	cmd.Dir = cloneDir
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("Error running hg clone on go.tools: %v\n%s", err, out)
	}
	if err := os.Rename(tmpDir, finalDir); err != nil {
		log.Fatal(err)
	}
	return gopath
}
<commit_msg>cmd/api: don't fail API check if there's no network<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.

// +build ignore

// The run program is invoked via "go run" from src/run.bash or
// src/run.bat conditionally builds and runs the cmd/api tool.
//
// TODO(bradfitz): the "conditional" condition is always true.
// We should only do this if the user has the hg codereview extension
// enabled and verifies that the go.tools subrepo is checked out with
// a suitably recently version. In prep for the cmd/api rewrite.
package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
)

// goToolsVersion is the hg revision of the go.tools subrepo we need
// to build cmd/api.  This only needs to be updated whenever a go/types
// bug fix is needed by the cmd/api tool.
const goToolsVersion = "6698ca2900e2"

var goroot string

func main() {
	log.SetFlags(0)
	goroot = os.Getenv("GOROOT") // should be set by run.{bash,bat}
	if goroot == "" {
		log.Fatal("No $GOROOT set.")
	}
	isGoDeveloper := exec.Command("hg", "pq").Run() == nil
	if !isGoDeveloper && !forceAPICheck() {
		fmt.Println("Skipping cmd/api checks; hg codereview extension not available and GO_FORCE_API_CHECK not set")
		return
	}

	gopath := prepGoPath()

	cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api")
	cmd.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Fatalf("Error installing cmd/api: %v\n%s", err, out)
	}

	out, err = exec.Command("go", "tool", "api",
		"-c", file("go1", "go1.1"),
		"-next", file("next"),
		"-except", file("except")).CombinedOutput()
	if err != nil {
		log.Fatalf("Error running API checker: %v\n%s", err, out)
	}
	fmt.Print(string(out))
}

// file expands s to $GOROOT/api/s.txt.
// If there are more than 1, they're comma-separated.
func file(s ...string) string {
	if len(s) > 1 {
		return file(s[0]) + "," + file(s[1:]...)
	}
	return filepath.Join(goroot, "api", s[0]+".txt")
}

// GO_FORCE_API_CHECK is set by builders.
func forceAPICheck() bool {
	v, _ := strconv.ParseBool(os.Getenv("GO_FORCE_API_CHECK"))
	return v
}

// prepGoPath returns a GOPATH for the "go" tool to compile the API tool with.
// It tries to re-use a go.tools checkout from a previous run if possible,
// else it hg clones it.
func prepGoPath() string {
	const tempBase = "go.tools.TMP"

	// The GOPATH we'll return
	gopath := filepath.Join(os.TempDir(), "gopath-api", goToolsVersion)

	// cloneDir is where we run "hg clone".
	cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")

	// The dir we clone into. We only atomically rename it to finalDir on
	// clone success.
	tmpDir := filepath.Join(cloneDir, tempBase)

	// finalDir is where the checkout will live once it's complete.
	// If this exists already, we're done.
	finalDir := filepath.Join(cloneDir, "go.tools")

	if fi, err := os.Stat(finalDir); err == nil && fi.IsDir() {
		return gopath
	}

	if err := os.MkdirAll(cloneDir, 0700); err != nil {
		log.Fatal(err)
	}
	cmd := exec.Command("hg",
		"clone", "--rev="+goToolsVersion,
		"https://code.google.com/p/go.tools",
		tempBase)
	cmd.Dir = cloneDir
	out, err := cmd.CombinedOutput()
	if err != nil {
		if _, err := http.Head("http://ip.appspot.com/"); err != nil {
			log.Printf("# Skipping API check; network appears to be unavailable")
			os.Exit(0)
		}
		log.Fatalf("Error running hg clone on go.tools: %v\n%s", err, out)
	}
	if err := os.Rename(tmpDir, finalDir); err != nil {
		log.Fatal(err)
	}
	return gopath
}
<|endoftext|>


<commit_before>// Copyright 2011 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

var helpImportpath = &Command{
	UsageLine: "importpath",
	Short:     "description of import paths",
	Long: `
Many commands apply to a set of packages named by import paths:

	go action [importpath...]

An import path that is a rooted path or that begins with
a . or .. element is interpreted as a file system path and
denotes the package in that directory.

Otherwise, the import path P denotes the package found in
the directory DIR/src/P for some DIR listed in the GOPATH
environment variable (see 'go help gopath').

If no import paths are given, the action applies to the
package in the current directory.

The special import path "all" expands to all package directories
found in all the GOPATH trees.  For example, 'go list all'
lists all the packages on the local system.

The special import path "std" is like all but expands to just the
packages in the standard Go library.

An import path can also name a package to be downloaded from
a remote repository.  Run 'go help remote' for details.

Every package in a program must have a unique import path.
By convention, this is arranged by starting each path with a
unique prefix that belongs to you.  For example, paths used
internally at Google all begin with 'google', and paths
denoting remote repositories begin with the path to the code,
such as 'project.googlecode.com/'.
	`,
}

var helpRemote = &Command{
	UsageLine: "remote",
	Short:     "remote import path syntax",
	Long: `

An import path (see 'go help importpath') denotes a package
stored in the local file system.  Certain import paths also
describe how to obtain the source code for the package using
a revision control system.

A few common code hosting sites have special syntax:

	BitBucket (Mercurial)

		import "bitbucket.org/user/project"
		import "bitbucket.org/user/project/sub/directory"

	GitHub (Git)

		import "github.com/user/project"
		import "github.com/user/project/sub/directory"

	Google Code Project Hosting (Git, Mercurial, Subversion)

		import "project.googlecode.com/git"
		import "project.googlecode.com/git/sub/directory"

		import "project.googlecode.com/hg"
		import "project.googlecode.com/hg/sub/directory"

		import "project.googlecode.com/svn/trunk"
		import "project.googlecode.com/svn/trunk/sub/directory"

	Launchpad (Bazaar)

		import "launchpad.net/project"
		import "launchpad.net/project/series"
		import "launchpad.net/project/series/sub/directory"

		import "launchpad.net/~user/project/branch"
		import "launchpad.net/~user/project/branch/sub/directory"

For code hosted on other servers, an import path of the form

	repository.vcs/path

specifies the given repository, with or without the .vcs suffix,
using the named version control system, and then the path inside
that repository.  The supported version control systems are:

	Bazaar      .bzr
	Git         .git
	Mercurial   .hg
	Subversion  .svn

For example,

	import "example.org/user/foo.hg"

denotes the root directory of the Mercurial repository at
example.org/user/foo or foo.hg, and

	import "example.org/repo.git/foo/bar"

denotes the foo/bar directory of the Git repository at
example.com/repo or repo.git.

When a version control system supports multiple protocols,
each is tried in turn when downloading.  For example, a Git
download tries git://, then https://, then http://.

New downloaded packages are written to the first directory
listed in the GOPATH environment variable (see 'go help gopath').

The go command attempts to download the version of the
package appropriate for the Go release being used.
Run 'go help install' for more.
	`,
}

var helpGopath = &Command{
	UsageLine: "gopath",
	Short:     "GOPATH environment variable",
	Long: `
The GOPATH environment variable lists places to look for Go code.
On Unix, the value is a colon-separated string.
On Windows, the value is a semicolon-separated string.
On Plan 9, the value is a list.

GOPATH must be set to build and install packages outside the
standard Go tree.

Each directory listed in GOPATH must have a prescribed structure:

The src/ directory holds source code.  The path below 'src'
determines the import path or executable name.

The pkg/ directory holds installed package objects.
As in the Go tree, each target operating system and
architecture pair has its own subdirectory of pkg
(pkg/GOOS_GOARCH).

If DIR is a directory listed in the GOPATH, a package with
source in DIR/src/foo/bar can be imported as "foo/bar" and
has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".

The bin/ directory holds compiled commands.
Each command is named for its source directory, but only
the final element, not the entire path.  That is, the
command with source in DIR/src/foo/quux is installed into
DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands.

Here's an example directory layout:

    GOPATH=/home/user/gocode

    /home/user/gocode/
        src/
            foo/
                bar/               (go code in package bar)
                    x.go
                quux/              (go code in package main)
                    y.go
        bin/
            quux                   (installed command)
		pkg/
		    linux_amd64/
		        foo/
		            bar.a          (installed package object)

Go searches each directory listed in GOPATH to find source code,
but new packages are always downloaded into the first directory
in the list.
	`,
}
<commit_msg>cmd/go: use spaces consistently in help message<commit_after>// Copyright 2011 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

var helpImportpath = &Command{
	UsageLine: "importpath",
	Short:     "description of import paths",
	Long: `
Many commands apply to a set of packages named by import paths:

	go action [importpath...]

An import path that is a rooted path or that begins with
a . or .. element is interpreted as a file system path and
denotes the package in that directory.

Otherwise, the import path P denotes the package found in
the directory DIR/src/P for some DIR listed in the GOPATH
environment variable (see 'go help gopath').

If no import paths are given, the action applies to the
package in the current directory.

The special import path "all" expands to all package directories
found in all the GOPATH trees.  For example, 'go list all'
lists all the packages on the local system.

The special import path "std" is like all but expands to just the
packages in the standard Go library.

An import path can also name a package to be downloaded from
a remote repository.  Run 'go help remote' for details.

Every package in a program must have a unique import path.
By convention, this is arranged by starting each path with a
unique prefix that belongs to you.  For example, paths used
internally at Google all begin with 'google', and paths
denoting remote repositories begin with the path to the code,
such as 'project.googlecode.com/'.
	`,
}

var helpRemote = &Command{
	UsageLine: "remote",
	Short:     "remote import path syntax",
	Long: `

An import path (see 'go help importpath') denotes a package
stored in the local file system.  Certain import paths also
describe how to obtain the source code for the package using
a revision control system.

A few common code hosting sites have special syntax:

	BitBucket (Mercurial)

		import "bitbucket.org/user/project"
		import "bitbucket.org/user/project/sub/directory"

	GitHub (Git)

		import "github.com/user/project"
		import "github.com/user/project/sub/directory"

	Google Code Project Hosting (Git, Mercurial, Subversion)

		import "project.googlecode.com/git"
		import "project.googlecode.com/git/sub/directory"

		import "project.googlecode.com/hg"
		import "project.googlecode.com/hg/sub/directory"

		import "project.googlecode.com/svn/trunk"
		import "project.googlecode.com/svn/trunk/sub/directory"

	Launchpad (Bazaar)

		import "launchpad.net/project"
		import "launchpad.net/project/series"
		import "launchpad.net/project/series/sub/directory"

		import "launchpad.net/~user/project/branch"
		import "launchpad.net/~user/project/branch/sub/directory"

For code hosted on other servers, an import path of the form

	repository.vcs/path

specifies the given repository, with or without the .vcs suffix,
using the named version control system, and then the path inside
that repository.  The supported version control systems are:

	Bazaar      .bzr
	Git         .git
	Mercurial   .hg
	Subversion  .svn

For example,

	import "example.org/user/foo.hg"

denotes the root directory of the Mercurial repository at
example.org/user/foo or foo.hg, and

	import "example.org/repo.git/foo/bar"

denotes the foo/bar directory of the Git repository at
example.com/repo or repo.git.

When a version control system supports multiple protocols,
each is tried in turn when downloading.  For example, a Git
download tries git://, then https://, then http://.

New downloaded packages are written to the first directory
listed in the GOPATH environment variable (see 'go help gopath').

The go command attempts to download the version of the
package appropriate for the Go release being used.
Run 'go help install' for more.
	`,
}

var helpGopath = &Command{
	UsageLine: "gopath",
	Short:     "GOPATH environment variable",
	Long: `
The GOPATH environment variable lists places to look for Go code.
On Unix, the value is a colon-separated string.
On Windows, the value is a semicolon-separated string.
On Plan 9, the value is a list.

GOPATH must be set to build and install packages outside the
standard Go tree.

Each directory listed in GOPATH must have a prescribed structure:

The src/ directory holds source code.  The path below 'src'
determines the import path or executable name.

The pkg/ directory holds installed package objects.
As in the Go tree, each target operating system and
architecture pair has its own subdirectory of pkg
(pkg/GOOS_GOARCH).

If DIR is a directory listed in the GOPATH, a package with
source in DIR/src/foo/bar can be imported as "foo/bar" and
has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a".

The bin/ directory holds compiled commands.
Each command is named for its source directory, but only
the final element, not the entire path.  That is, the
command with source in DIR/src/foo/quux is installed into
DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
so that you can add DIR/bin to your PATH to get at the
installed commands.

Here's an example directory layout:

    GOPATH=/home/user/gocode

    /home/user/gocode/
        src/
            foo/
                bar/               (go code in package bar)
                    x.go
                quux/              (go code in package main)
                    y.go
        bin/
            quux                   (installed command)
        pkg/
            linux_amd64/
                foo/
                    bar.a          (installed package object)

Go searches each directory listed in GOPATH to find source code,
but new packages are always downloaded into the first directory
in the list.
	`,
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/hashicorp/vault/api"
	"github.com/mitchellh/cli"
	"github.com/ryanuber/columnize"
)

func OutputSecret(ui cli.Ui, format string, secret *api.Secret) int {
	return outputWithFormat(ui, format, secret, secret)
}

func OutputList(ui cli.Ui, format string, secret *api.Secret) int {
	return outputWithFormat(ui, format, secret, secret.Data["keys"])
}

func outputWithFormat(ui cli.Ui, format string, secret *api.Secret, data interface{}) int {
	formatter, ok := Formatters[strings.ToLower(format)]
	if !ok {
		ui.Error(fmt.Sprintf("Invalid output format: %s", format))
		return 1
	}
	if err := formatter.Output(ui, secret, data); err != nil {
		ui.Error(fmt.Sprintf("Could not output secret: %s", err.Error()))
		return 1
	}
	return 0
}

type Formatter interface {
	Output(ui cli.Ui, secret *api.Secret, data interface{}) error
}

var Formatters = map[string]Formatter{
	"json":  JsonFormatter{},
	"table": TableFormatter{},
	"yaml":  YamlFormatter{},
}

// An output formatter for json output of an object
type JsonFormatter struct {
}

func (j JsonFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
	b, err := json.Marshal(data)
	if err == nil {
		var out bytes.Buffer
		json.Indent(&out, b, "", "\t")
		ui.Output(out.String())
	}
	return err
}

// An output formatter for yaml output format of an object
type YamlFormatter struct {
}

func (y YamlFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
	b, err := yaml.Marshal(data)
	if err == nil {
		ui.Output(strings.TrimSpace(string(b)))
	}
	return err
}

// An output formatter for table output of an object
type TableFormatter struct {
}

func (t TableFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
	// TODO: this should really use reflection like the other formatters do
	if s, ok := data.(*api.Secret); ok {
		return t.OutputSecret(ui, secret, s)
	}
	if s, ok := data.([]interface{}); ok {
		return t.OutputList(ui, secret, s)
	}
	return errors.New("Cannot use the table formatter for this type")
}

func (t TableFormatter) OutputList(ui cli.Ui, secret *api.Secret, list []interface{}) error {
	config := columnize.DefaultConfig()
	config.Delim = "♨"
	config.Glue = "\t"
	config.Prefix = ""

	input := make([]string, 0, 5)

	input = append(input, "Keys")

	keys := make([]string, 0, len(list))
	for _, k := range list {
		keys = append(keys, k.(string))
	}
	sort.Strings(keys)

	for _, k := range keys {
		input = append(input, fmt.Sprintf("%s", k))
	}

	if len(secret.Warnings) != 0 {
		input = append(input, "")
		input = append(input, "The following warnings were returned from the Vault server:")
		for _, warning := range secret.Warnings {
			input = append(input, fmt.Sprintf("* %s", warning))
		}
	}

	ui.Output(columnize.Format(input, config))
	return nil
}

func (t TableFormatter) OutputSecret(ui cli.Ui, secret, s *api.Secret) error {
	config := columnize.DefaultConfig()
	config.Delim = "♨"
	config.Glue = "\t"
	config.Prefix = ""

	input := make([]string, 0, 5)

	input = append(input, fmt.Sprintf("Key %s Value", config.Delim))

	if s.LeaseDuration > 0 {
		if s.LeaseID != "" {
			input = append(input, fmt.Sprintf("lease_id %s %s", config.Delim, s.LeaseID))
		}
		input = append(input, fmt.Sprintf(
			"lease_duration %s %d", config.Delim, s.LeaseDuration))
		if s.LeaseID != "" {
			input = append(input, fmt.Sprintf(
				"lease_renewable %s %s", config.Delim, strconv.FormatBool(s.Renewable)))
		}
	}

	if s.Auth != nil {
		input = append(input, fmt.Sprintf("token %s %s", config.Delim, s.Auth.ClientToken))
		input = append(input, fmt.Sprintf("token_accessor %s %s", config.Delim, s.Auth.Accessor))
		input = append(input, fmt.Sprintf("token_duration %s %d", config.Delim, s.Auth.LeaseDuration))
		input = append(input, fmt.Sprintf("token_renewable %s %v", config.Delim, s.Auth.Renewable))
		input = append(input, fmt.Sprintf("token_policies %s %v", config.Delim, s.Auth.Policies))
		for k, v := range s.Auth.Metadata {
			input = append(input, fmt.Sprintf("token_meta_%s %s %#v", k, config.Delim, v))
		}
	}

	keys := make([]string, 0, len(s.Data))
	for k := range s.Data {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		input = append(input, fmt.Sprintf("%s %s %v", k, config.Delim, s.Data[k]))
	}

	if len(s.Warnings) != 0 {
		input = append(input, "")
		input = append(input, "The following warnings were returned from the Vault server:")
		for _, warning := range s.Warnings {
			input = append(input, fmt.Sprintf("* %s", warning))
		}
	}

	ui.Output(columnize.Format(input, config))
	return nil
}
<commit_msg>Rename lease_duration to refresh_interval when there is no lease ID, and output ---- between header and values<commit_after>package command

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"sort"
	"strconv"
	"strings"

	"github.com/ghodss/yaml"
	"github.com/hashicorp/vault/api"
	"github.com/mitchellh/cli"
	"github.com/ryanuber/columnize"
)

func OutputSecret(ui cli.Ui, format string, secret *api.Secret) int {
	return outputWithFormat(ui, format, secret, secret)
}

func OutputList(ui cli.Ui, format string, secret *api.Secret) int {
	return outputWithFormat(ui, format, secret, secret.Data["keys"])
}

func outputWithFormat(ui cli.Ui, format string, secret *api.Secret, data interface{}) int {
	formatter, ok := Formatters[strings.ToLower(format)]
	if !ok {
		ui.Error(fmt.Sprintf("Invalid output format: %s", format))
		return 1
	}
	if err := formatter.Output(ui, secret, data); err != nil {
		ui.Error(fmt.Sprintf("Could not output secret: %s", err.Error()))
		return 1
	}
	return 0
}

type Formatter interface {
	Output(ui cli.Ui, secret *api.Secret, data interface{}) error
}

var Formatters = map[string]Formatter{
	"json":  JsonFormatter{},
	"table": TableFormatter{},
	"yaml":  YamlFormatter{},
}

// An output formatter for json output of an object
type JsonFormatter struct {
}

func (j JsonFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
	b, err := json.Marshal(data)
	if err == nil {
		var out bytes.Buffer
		json.Indent(&out, b, "", "\t")
		ui.Output(out.String())
	}
	return err
}

// An output formatter for yaml output format of an object
type YamlFormatter struct {
}

func (y YamlFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
	b, err := yaml.Marshal(data)
	if err == nil {
		ui.Output(strings.TrimSpace(string(b)))
	}
	return err
}

// An output formatter for table output of an object
type TableFormatter struct {
}

func (t TableFormatter) Output(ui cli.Ui, secret *api.Secret, data interface{}) error {
	// TODO: this should really use reflection like the other formatters do
	if s, ok := data.(*api.Secret); ok {
		return t.OutputSecret(ui, secret, s)
	}
	if s, ok := data.([]interface{}); ok {
		return t.OutputList(ui, secret, s)
	}
	return errors.New("Cannot use the table formatter for this type")
}

func (t TableFormatter) OutputList(ui cli.Ui, secret *api.Secret, list []interface{}) error {
	config := columnize.DefaultConfig()
	config.Delim = "♨"
	config.Glue = "\t"
	config.Prefix = ""

	input := make([]string, 0, 5)

	input = append(input, "Keys")

	keys := make([]string, 0, len(list))
	for _, k := range list {
		keys = append(keys, k.(string))
	}
	sort.Strings(keys)

	for _, k := range keys {
		input = append(input, fmt.Sprintf("%s", k))
	}

	if len(secret.Warnings) != 0 {
		input = append(input, "")
		input = append(input, "The following warnings were returned from the Vault server:")
		for _, warning := range secret.Warnings {
			input = append(input, fmt.Sprintf("* %s", warning))
		}
	}

	ui.Output(columnize.Format(input, config))
	return nil
}

func (t TableFormatter) OutputSecret(ui cli.Ui, secret, s *api.Secret) error {
	config := columnize.DefaultConfig()
	config.Delim = "♨"
	config.Glue = "\t"
	config.Prefix = ""

	input := make([]string, 0, 5)

	input = append(input, fmt.Sprintf("Key %s Value", config.Delim))

	input = append(input, fmt.Sprintf("--- %s -----", config.Delim))

	if s.LeaseDuration > 0 {
		if s.LeaseID != "" {
			input = append(input, fmt.Sprintf("lease_id %s %s", config.Delim, s.LeaseID))
			input = append(input, fmt.Sprintf(
				"lease_duration %s %d", config.Delim, s.LeaseDuration))
		} else {
			input = append(input, fmt.Sprintf(
				"refresh_interval %s %d", config.Delim, s.LeaseDuration))
		}
		if s.LeaseID != "" {
			input = append(input, fmt.Sprintf(
				"lease_renewable %s %s", config.Delim, strconv.FormatBool(s.Renewable)))
		}
	}

	if s.Auth != nil {
		input = append(input, fmt.Sprintf("token %s %s", config.Delim, s.Auth.ClientToken))
		input = append(input, fmt.Sprintf("token_accessor %s %s", config.Delim, s.Auth.Accessor))
		input = append(input, fmt.Sprintf("token_duration %s %d", config.Delim, s.Auth.LeaseDuration))
		input = append(input, fmt.Sprintf("token_renewable %s %v", config.Delim, s.Auth.Renewable))
		input = append(input, fmt.Sprintf("token_policies %s %v", config.Delim, s.Auth.Policies))
		for k, v := range s.Auth.Metadata {
			input = append(input, fmt.Sprintf("token_meta_%s %s %#v", k, config.Delim, v))
		}
	}

	keys := make([]string, 0, len(s.Data))
	for k := range s.Data {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for _, k := range keys {
		input = append(input, fmt.Sprintf("%s %s %v", k, config.Delim, s.Data[k]))
	}

	if len(s.Warnings) != 0 {
		input = append(input, "")
		input = append(input, "The following warnings were returned from the Vault server:")
		for _, warning := range s.Warnings {
			input = append(input, fmt.Sprintf("* %s", warning))
		}
	}

	ui.Output(columnize.Format(input, config))
	return nil
}
<|endoftext|>


<commit_before>package command

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	. "github.com/ForceCLI/force/error"
	. "github.com/ForceCLI/force/lib"
)

var cmdImport = &Command{
	Usage: "import [deployment options]",
	Short: "Import metadata from a local directory",
	Long: `
Import metadata from a local directory

Deployment Options
  -rollbackonerror, -r    Indicates whether any failure causes a complete rollback
  -runalltests, -t        If set all Apex tests defined in the organization are run (equivalent to -l RunAllTestsInOrg)
  -checkonly, -c          Indicates whether classes and triggers are saved during deployment
  -purgeondelete, -p      If set the deleted components are not stored in recycle bin
  -allowmissingfiles, -m  Specifies whether a deploy succeeds even if files missing
  -autoupdatepackage, -u  Auto add files to the package if missing
  -test                   Run tests in class (implies -l RunSpecifiedTests)
  -testlevel, -l          Set test level (NoTestRun, RunSpecifiedTests, RunLocalTests, RunAllTestsInOrg)
  -ignorewarnings, -i     Indicates if warnings should fail deployment or not
  -directory, -d 		  Path to the package.xml file to import
  -verbose, -v 			  Provide detailed feedback on operation

Examples:

  force import

  force import -directory=my_metadata -c -r -v

  force import -checkonly -runalltests
`,
}

var (
	testsToRun            metaName
	rollBackOnErrorFlag   = cmdImport.Flag.Bool("rollbackonerror", false, "set roll back on error")
	runAllTestsFlag       = cmdImport.Flag.Bool("runalltests", false, "set run all tests")
	testLevelFlag         = cmdImport.Flag.String("testLevel", "NoTestRun", "set test level")
	checkOnlyFlag         = cmdImport.Flag.Bool("checkonly", false, "set check only")
	purgeOnDeleteFlag     = cmdImport.Flag.Bool("purgeondelete", false, "set purge on delete")
	allowMissingFilesFlag = cmdImport.Flag.Bool("allowmissingfiles", false, "set allow missing files")
	autoUpdatePackageFlag = cmdImport.Flag.Bool("autoupdatepackage", false, "set auto update package")
	ignoreWarningsFlag    = cmdImport.Flag.Bool("ignorewarnings", false, "set ignore warnings")
	directory             = cmdImport.Flag.String("directory", "metadata", "relative path to package.xml")
	verbose               = cmdImport.Flag.Bool("verbose", false, "give more verbose output")
)

func init() {
	cmdImport.Run = runImport
	cmdImport.Flag.BoolVar(verbose, "v", false, "give more verbose output")
	cmdImport.Flag.BoolVar(rollBackOnErrorFlag, "r", false, "set roll back on error")
	cmdImport.Flag.BoolVar(runAllTestsFlag, "t", false, "set run all tests")
	cmdImport.Flag.StringVar(testLevelFlag, "l", "NoTestRun", "set test level")
	cmdImport.Flag.BoolVar(checkOnlyFlag, "c", false, "set check only")
	cmdImport.Flag.BoolVar(purgeOnDeleteFlag, "p", false, "set purge on delete")
	cmdImport.Flag.BoolVar(allowMissingFilesFlag, "m", false, "set allow missing files")
	cmdImport.Flag.BoolVar(autoUpdatePackageFlag, "u", false, "set auto update package")
	cmdImport.Flag.BoolVar(ignoreWarningsFlag, "i", false, "set ignore warnings")
	cmdImport.Flag.StringVar(directory, "d", "metadata", "relative path to package.xml")
	cmdImport.Flag.Var(&testsToRun, "test", "Test(s) to run")
}

func runImport(cmd *Command, args []string) {
	if len(args) > 0 {
		ErrorAndExit("Unrecognized argument: " + args[0])
	}

	wd, _ := os.Getwd()
	usr, err := user.Current()
	var dir string

	//Manually handle shell expansion short cut
	if err != nil {
		if strings.HasPrefix(*directory, "~") {
			ErrorAndExit("Cannot determine tilde expansion, please use relative or absolute path to directory.")
		} else {
			dir = *directory
		}
	} else {
		if strings.HasPrefix(*directory, "~") {
			dir = strings.Replace(*directory, "~", usr.HomeDir, 1)
		} else {
			dir = *directory
		}
	}

	root := filepath.Join(wd, dir)

	// Check for absolute path
	if filepath.IsAbs(dir) {
		root = dir
	}

	force, _ := ActiveForce()
	files := make(ForceMetadataFiles)
	if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
		ErrorAndExit(" \n" + filepath.Join(root, "package.xml") + "\ndoes not exist")
	}

	err = filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
			if f.Name() != ".DS_Store" {
				data, err := ioutil.ReadFile(path)
				if err != nil {
					ErrorAndExit(err.Error())
				}
				files[strings.Replace(path, fmt.Sprintf("%s%s", root, string(os.PathSeparator)), "", -1)] = data
			}
		}
		return nil
	})
	if err != nil {
		ErrorAndExit(err.Error())
	}
	var DeploymentOptions ForceDeployOptions
	DeploymentOptions.AllowMissingFiles = *allowMissingFilesFlag
	DeploymentOptions.AutoUpdatePackage = *autoUpdatePackageFlag
	DeploymentOptions.CheckOnly = *checkOnlyFlag
	DeploymentOptions.IgnoreWarnings = *ignoreWarningsFlag
	DeploymentOptions.PurgeOnDelete = *purgeOnDeleteFlag
	DeploymentOptions.RollbackOnError = *rollBackOnErrorFlag
	DeploymentOptions.TestLevel = *testLevelFlag
	if *runAllTestsFlag {
		DeploymentOptions.TestLevel = "RunAllTestsInOrg"
	}
	DeploymentOptions.RunTests = testsToRun

	result, err := force.Metadata.Deploy(files, DeploymentOptions)
	problems := result.Details.ComponentFailures
	successes := result.Details.ComponentSuccesses
	testFailures := result.Details.RunTestResult.TestFailures
	testSuccesses := result.Details.RunTestResult.TestSuccesses
	codeCoverageWarnings := result.Details.RunTestResult.CodeCoverageWarnings
	if err != nil {
		ErrorAndExit(err.Error())
	}

	fmt.Printf("\nFailures - %d\n", len(problems))
	if *verbose {
		for _, problem := range problems {
			if problem.FullName == "" {
				fmt.Println(problem.Problem)
			} else {
				fmt.Printf("%s: %s\n", problem.FullName, problem.Problem)
			}
		}
	}

	fmt.Printf("\nSuccesses - %d\n", len(successes))
	if *verbose {
		for _, success := range successes {
			if success.FullName != "package.xml" {
				verb := "unchanged"
				if success.Changed {
					verb = "changed"
				} else if success.Deleted {
					verb = "deleted"
				} else if success.Created {
					verb = "created"
				}
				fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
			}
		}
		fmt.Printf("\nTest Successes - %d\n", len(testSuccesses))
		for _, failure := range testSuccesses {
			fmt.Printf("  [PASS]  %s::%s\n", failure.Name, failure.MethodName)
		}

		fmt.Printf("\nTest Failures - %d\n", len(testFailures))
		for _, failure := range testFailures {
			fmt.Printf("\n  [FAIL]  %s::%s: %s\n", failure.Name, failure.MethodName, failure.Message)
			fmt.Println(failure.StackTrace)
		}

		if len(codeCoverageWarnings) > 0 {
			fmt.Printf("\nCode Coverage Warnings - %d\n", len(codeCoverageWarnings))
			for _, warning := range codeCoverageWarnings {
				fmt.Printf("\n %s: %s\n", warning.Name, warning.Message)
			}
		}

	}
	if len(problems) > 0 {
		err = errors.New("Some components failed deployment")
	} else if len(testFailures) > 0 {
		err = errors.New("Some tests failed")
	} else if !result.Success {
		err = errors.New(fmt.Sprintf("Status: %s", result.Status))
	}
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Printf("Imported from %s\n", root)
}
<commit_msg>Make force import Output Consistent With force push<commit_after>package command

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	. "github.com/ForceCLI/force/error"
	. "github.com/ForceCLI/force/lib"
)

var cmdImport = &Command{
	Usage: "import [deployment options]",
	Short: "Import metadata from a local directory",
	Long: `
Import metadata from a local directory

Deployment Options
  -rollbackonerror, -r    Indicates whether any failure causes a complete rollback
  -runalltests, -t        If set all Apex tests defined in the organization are run (equivalent to -l RunAllTestsInOrg)
  -checkonly, -c          Indicates whether classes and triggers are saved during deployment
  -purgeondelete, -p      If set the deleted components are not stored in recycle bin
  -allowmissingfiles, -m  Specifies whether a deploy succeeds even if files missing
  -autoupdatepackage, -u  Auto add files to the package if missing
  -test                   Run tests in class (implies -l RunSpecifiedTests)
  -testlevel, -l          Set test level (NoTestRun, RunSpecifiedTests, RunLocalTests, RunAllTestsInOrg)
  -ignorewarnings, -i     Indicates if warnings should fail deployment or not
  -directory, -d 		  Path to the package.xml file to import
  -verbose, -v 			  Provide detailed feedback on operation

Examples:

  force import

  force import -directory=my_metadata -c -r -v

  force import -checkonly -runalltests
`,
}

var (
	testsToRun            metaName
	rollBackOnErrorFlag   = cmdImport.Flag.Bool("rollbackonerror", false, "set roll back on error")
	runAllTestsFlag       = cmdImport.Flag.Bool("runalltests", false, "set run all tests")
	testLevelFlag         = cmdImport.Flag.String("testLevel", "NoTestRun", "set test level")
	checkOnlyFlag         = cmdImport.Flag.Bool("checkonly", false, "set check only")
	purgeOnDeleteFlag     = cmdImport.Flag.Bool("purgeondelete", false, "set purge on delete")
	allowMissingFilesFlag = cmdImport.Flag.Bool("allowmissingfiles", false, "set allow missing files")
	autoUpdatePackageFlag = cmdImport.Flag.Bool("autoupdatepackage", false, "set auto update package")
	ignoreWarningsFlag    = cmdImport.Flag.Bool("ignorewarnings", false, "set ignore warnings")
	directory             = cmdImport.Flag.String("directory", "metadata", "relative path to package.xml")
	verbose               = cmdImport.Flag.Bool("verbose", false, "give more verbose output")
)

func init() {
	cmdImport.Run = runImport
	cmdImport.Flag.BoolVar(verbose, "v", false, "give more verbose output")
	cmdImport.Flag.BoolVar(rollBackOnErrorFlag, "r", false, "set roll back on error")
	cmdImport.Flag.BoolVar(runAllTestsFlag, "t", false, "set run all tests")
	cmdImport.Flag.StringVar(testLevelFlag, "l", "NoTestRun", "set test level")
	cmdImport.Flag.BoolVar(checkOnlyFlag, "c", false, "set check only")
	cmdImport.Flag.BoolVar(purgeOnDeleteFlag, "p", false, "set purge on delete")
	cmdImport.Flag.BoolVar(allowMissingFilesFlag, "m", false, "set allow missing files")
	cmdImport.Flag.BoolVar(autoUpdatePackageFlag, "u", false, "set auto update package")
	cmdImport.Flag.BoolVar(ignoreWarningsFlag, "i", false, "set ignore warnings")
	cmdImport.Flag.StringVar(directory, "d", "metadata", "relative path to package.xml")
	cmdImport.Flag.Var(&testsToRun, "test", "Test(s) to run")
}

func runImport(cmd *Command, args []string) {
	if len(args) > 0 {
		ErrorAndExit("Unrecognized argument: " + args[0])
	}

	wd, _ := os.Getwd()
	usr, err := user.Current()
	var dir string

	//Manually handle shell expansion short cut
	if err != nil {
		if strings.HasPrefix(*directory, "~") {
			ErrorAndExit("Cannot determine tilde expansion, please use relative or absolute path to directory.")
		} else {
			dir = *directory
		}
	} else {
		if strings.HasPrefix(*directory, "~") {
			dir = strings.Replace(*directory, "~", usr.HomeDir, 1)
		} else {
			dir = *directory
		}
	}

	root := filepath.Join(wd, dir)

	// Check for absolute path
	if filepath.IsAbs(dir) {
		root = dir
	}

	force, _ := ActiveForce()
	files := make(ForceMetadataFiles)
	if _, err := os.Stat(filepath.Join(root, "package.xml")); os.IsNotExist(err) {
		ErrorAndExit(" \n" + filepath.Join(root, "package.xml") + "\ndoes not exist")
	}

	err = filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
		if f.Mode().IsRegular() {
			if f.Name() != ".DS_Store" {
				data, err := ioutil.ReadFile(path)
				if err != nil {
					ErrorAndExit(err.Error())
				}
				files[strings.Replace(path, fmt.Sprintf("%s%s", root, string(os.PathSeparator)), "", -1)] = data
			}
		}
		return nil
	})
	if err != nil {
		ErrorAndExit(err.Error())
	}
	var DeploymentOptions ForceDeployOptions
	DeploymentOptions.AllowMissingFiles = *allowMissingFilesFlag
	DeploymentOptions.AutoUpdatePackage = *autoUpdatePackageFlag
	DeploymentOptions.CheckOnly = *checkOnlyFlag
	DeploymentOptions.IgnoreWarnings = *ignoreWarningsFlag
	DeploymentOptions.PurgeOnDelete = *purgeOnDeleteFlag
	DeploymentOptions.RollbackOnError = *rollBackOnErrorFlag
	DeploymentOptions.TestLevel = *testLevelFlag
	if *runAllTestsFlag {
		DeploymentOptions.TestLevel = "RunAllTestsInOrg"
	}
	DeploymentOptions.RunTests = testsToRun

	result, err := force.Metadata.Deploy(files, DeploymentOptions)
	problems := result.Details.ComponentFailures
	successes := result.Details.ComponentSuccesses
	testFailures := result.Details.RunTestResult.TestFailures
	testSuccesses := result.Details.RunTestResult.TestSuccesses
	codeCoverageWarnings := result.Details.RunTestResult.CodeCoverageWarnings
	if err != nil {
		ErrorAndExit(err.Error())
	}

	fmt.Printf("\nSuccesses - %d\n", len(successes))
	if *verbose {
		for _, success := range successes {
			if success.FullName != "package.xml" {
				verb := "unchanged"
				if success.Changed {
					verb = "changed"
				} else if success.Deleted {
					verb = "deleted"
				} else if success.Created {
					verb = "created"
				}
				fmt.Printf("%s\n\tstatus: %s\n\tid=%s\n", success.FullName, verb, success.Id)
			}
		}

	}

	fmt.Printf("\nTest Successes - %d\n", len(testSuccesses))
	for _, failure := range testSuccesses {
		fmt.Printf("  [PASS]  %s::%s\n", failure.Name, failure.MethodName)
	}

	fmt.Printf("\nFailures - %d\n", len(problems))
	for _, problem := range problems {
		if problem.FullName == "" {
			fmt.Println(problem.Problem)
		} else {
			fmt.Printf("\"%s\", line %d: %s %s\n", problem.FullName, problem.LineNumber, problem.ProblemType, problem.Problem)
		}
	}

	fmt.Printf("\nTest Failures - %d\n", len(testFailures))
	for _, failure := range testFailures {
		fmt.Printf("\n  [FAIL]  %s::%s: %s\n", failure.Name, failure.MethodName, failure.Message)
		fmt.Println(failure.StackTrace)
	}

	if len(codeCoverageWarnings) > 0 {
		fmt.Printf("\nCode Coverage Warnings - %d\n", len(codeCoverageWarnings))
		for _, warning := range codeCoverageWarnings {
			fmt.Printf("\n %s: %s\n", warning.Name, warning.Message)
		}
	}

	if len(problems) > 0 {
		err = errors.New("Some components failed deployment")
	} else if len(testFailures) > 0 {
		err = errors.New("Some tests failed")
	} else if !result.Success {
		err = errors.New(fmt.Sprintf("Status: %s", result.Status))
	}
	if err != nil {
		ErrorAndExit(err.Error())
	}
	fmt.Printf("Imported from %s\n", root)
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/hex"
	"fmt"
	"net"
	"net/http"
	"strings"

	"github.com/hashicorp/vault/command/server"
	"github.com/hashicorp/vault/helper/flag-slice"
	vaulthttp "github.com/hashicorp/vault/http"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/physical"
	"github.com/hashicorp/vault/vault"
)

// ServerCommand is a Command that starts the Vault server.
type ServerCommand struct {
	LogicalBackends map[string]logical.Factory

	Meta
}

func (c *ServerCommand) Run(args []string) int {
	var dev bool
	var configPath []string
	flags := c.Meta.FlagSet("server", FlagSetDefault)
	flags.BoolVar(&dev, "dev", false, "")
	flags.Usage = func() { c.Ui.Error(c.Help()) }
	flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Validation
	if !dev && len(configPath) == 0 {
		c.Ui.Error("At least one config path must be specified with -config")
		flags.Usage()
		return 1
	}

	// Load the configuration
	var config *server.Config
	if dev {
		config = server.DevConfig()
	}
	for _, path := range configPath {
		current, err := server.LoadConfig(path)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error loading configuration from %s: %s", path, err))
			return 1
		}

		if config == nil {
			config = current
		} else {
			config = config.Merge(current)
		}
	}

	// Initialize the backend
	backend, err := physical.NewBackend(
		config.Backend.Type, config.Backend.Config)
	if err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error initializing backend of type %s: %s",
			config.Backend.Type, err))
		return 1
	}

	// Initialize the core
	core, err := vault.NewCore(&vault.CoreConfig{
		Physical:        backend,
		LogicalBackends: c.LogicalBackends,
	})

	// If we're in dev mode, then initialize the core
	if dev {
		init, err := c.enableDev(core)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error initializing dev mode: %s", err))
			return 1
		}

		c.Ui.Output(fmt.Sprintf(
			"WARNING: Dev mode is enabled!\n\n"+
				"In this mode, Vault is completely in-memory and unsealed.\n"+
				"Vault is configured to only have a single unseal key. The root\n"+
				"token has already been authenticated with the CLI, so you can\n"+
				"immediately begin using the Vault CLI.\n\n"+
				"The unseal key and root token are reproduced below in case you\n"+
				"want to seal/unseal the Vault or play with authentication.\n\n"+
				"Unseal Key: %s\nRoot Token: %s\n\n",
			hex.EncodeToString(init.SecretShares[0]),
			init.RootToken,
		))
	}

	// Initialize the listeners
	lns := make([]net.Listener, 0, len(config.Listeners))
	for _, lnConfig := range config.Listeners {
		ln, err := server.NewListener(lnConfig.Type, lnConfig.Config)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error initializing listener of type %s: %s",
				lnConfig.Type, err))
			return 1
		}

		lns = append(lns, ln)
	}

	// Initialize the HTTP server
	server := &http.Server{}
	server.Handler = vaulthttp.Handler(core)
	for _, ln := range lns {
		go server.Serve(ln)
	}

	<-make(chan struct{})
	return 0
}

func (c *ServerCommand) enableDev(core *vault.Core) (*vault.InitResult, error) {
	// Initialize it with a basic single key
	init, err := core.Initialize(&vault.SealConfig{
		SecretShares:    1,
		SecretThreshold: 1,
	})
	if err != nil {
		return nil, err
	}

	// Copy the key so that it can be zeroed
	key := make([]byte, len(init.SecretShares[0]))
	copy(key, init.SecretShares[0])

	// Unseal the core
	unsealed, err := core.Unseal(key)
	if err != nil {
		return nil, err
	}
	if !unsealed {
		return nil, fmt.Errorf("failed to unseal Vault for dev mode")
	}

	// Set the token
	tokenHelper, err := c.TokenHelper()
	if err != nil {
		return nil, err
	}
	if err := tokenHelper.Store(init.RootToken); err != nil {
		return nil, err
	}

	return init, nil
}

func (c *ServerCommand) Synopsis() string {
	return "Start a Vault server"
}

func (c *ServerCommand) Help() string {
	helpText := `
Usage: vault server [options]

  Start a Vault server.

  This command starts a Vault server that responds to API requests.
  Vault will start in a "sealed" state. The Vault must be unsealed
  with "vault unseal" or the API before this server can respond to requests.
  This must be done for every server.

  If the server is being started against a storage backend that has
  brand new (no existing Vault data in it), it must be initialized with
  "vault init" or the API first.

General Options:

  -config=<path>      Path to the configuration file or directory. This can be
                      specified multiple times. If it is a directory, all
                      files with a ".hcl" or ".json" suffix will be loaded.

`
	return strings.TrimSpace(helpText)
}
<commit_msg>command/server: support CredentialBackends<commit_after>package command

import (
	"encoding/hex"
	"fmt"
	"net"
	"net/http"
	"strings"

	"github.com/hashicorp/vault/command/server"
	"github.com/hashicorp/vault/helper/flag-slice"
	vaulthttp "github.com/hashicorp/vault/http"
	"github.com/hashicorp/vault/logical"
	"github.com/hashicorp/vault/physical"
	"github.com/hashicorp/vault/vault"
)

// ServerCommand is a Command that starts the Vault server.
type ServerCommand struct {
	CredentialBackends map[string]logical.Factory
	LogicalBackends    map[string]logical.Factory

	Meta
}

func (c *ServerCommand) Run(args []string) int {
	var dev bool
	var configPath []string
	flags := c.Meta.FlagSet("server", FlagSetDefault)
	flags.BoolVar(&dev, "dev", false, "")
	flags.Usage = func() { c.Ui.Error(c.Help()) }
	flags.Var((*sliceflag.StringFlag)(&configPath), "config", "config")
	if err := flags.Parse(args); err != nil {
		return 1
	}

	// Validation
	if !dev && len(configPath) == 0 {
		c.Ui.Error("At least one config path must be specified with -config")
		flags.Usage()
		return 1
	}

	// Load the configuration
	var config *server.Config
	if dev {
		config = server.DevConfig()
	}
	for _, path := range configPath {
		current, err := server.LoadConfig(path)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error loading configuration from %s: %s", path, err))
			return 1
		}

		if config == nil {
			config = current
		} else {
			config = config.Merge(current)
		}
	}

	// Initialize the backend
	backend, err := physical.NewBackend(
		config.Backend.Type, config.Backend.Config)
	if err != nil {
		c.Ui.Error(fmt.Sprintf(
			"Error initializing backend of type %s: %s",
			config.Backend.Type, err))
		return 1
	}

	// Initialize the core
	core, err := vault.NewCore(&vault.CoreConfig{
		Physical:           backend,
		CredentialBackends: c.CredentialBackends,
		LogicalBackends:    c.LogicalBackends,
	})

	// If we're in dev mode, then initialize the core
	if dev {
		init, err := c.enableDev(core)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error initializing dev mode: %s", err))
			return 1
		}

		c.Ui.Output(fmt.Sprintf(
			"WARNING: Dev mode is enabled!\n\n"+
				"In this mode, Vault is completely in-memory and unsealed.\n"+
				"Vault is configured to only have a single unseal key. The root\n"+
				"token has already been authenticated with the CLI, so you can\n"+
				"immediately begin using the Vault CLI.\n\n"+
				"The unseal key and root token are reproduced below in case you\n"+
				"want to seal/unseal the Vault or play with authentication.\n\n"+
				"Unseal Key: %s\nRoot Token: %s\n\n",
			hex.EncodeToString(init.SecretShares[0]),
			init.RootToken,
		))
	}

	// Initialize the listeners
	lns := make([]net.Listener, 0, len(config.Listeners))
	for _, lnConfig := range config.Listeners {
		ln, err := server.NewListener(lnConfig.Type, lnConfig.Config)
		if err != nil {
			c.Ui.Error(fmt.Sprintf(
				"Error initializing listener of type %s: %s",
				lnConfig.Type, err))
			return 1
		}

		lns = append(lns, ln)
	}

	// Initialize the HTTP server
	server := &http.Server{}
	server.Handler = vaulthttp.Handler(core)
	for _, ln := range lns {
		go server.Serve(ln)
	}

	<-make(chan struct{})
	return 0
}

func (c *ServerCommand) enableDev(core *vault.Core) (*vault.InitResult, error) {
	// Initialize it with a basic single key
	init, err := core.Initialize(&vault.SealConfig{
		SecretShares:    1,
		SecretThreshold: 1,
	})
	if err != nil {
		return nil, err
	}

	// Copy the key so that it can be zeroed
	key := make([]byte, len(init.SecretShares[0]))
	copy(key, init.SecretShares[0])

	// Unseal the core
	unsealed, err := core.Unseal(key)
	if err != nil {
		return nil, err
	}
	if !unsealed {
		return nil, fmt.Errorf("failed to unseal Vault for dev mode")
	}

	// Set the token
	tokenHelper, err := c.TokenHelper()
	if err != nil {
		return nil, err
	}
	if err := tokenHelper.Store(init.RootToken); err != nil {
		return nil, err
	}

	return init, nil
}

func (c *ServerCommand) Synopsis() string {
	return "Start a Vault server"
}

func (c *ServerCommand) Help() string {
	helpText := `
Usage: vault server [options]

  Start a Vault server.

  This command starts a Vault server that responds to API requests.
  Vault will start in a "sealed" state. The Vault must be unsealed
  with "vault unseal" or the API before this server can respond to requests.
  This must be done for every server.

  If the server is being started against a storage backend that has
  brand new (no existing Vault data in it), it must be initialized with
  "vault init" or the API first.

General Options:

  -config=<path>      Path to the configuration file or directory. This can be
                      specified multiple times. If it is a directory, all
                      files with a ".hcl" or ".json" suffix will be loaded.

`
	return strings.TrimSpace(helpText)
}
<|endoftext|>