<commit_before>// Inspired by https://www.goinggo.net/2013/11/using-log-package-in-go.html
package log

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"
)

var (
	Debug       *log.Logger
	Info        *log.Logger
	Warning     *log.Logger
	Error       *log.Logger
	Event       *log.Logger
	debugCloser io.Closer
	infoCloser  io.Closer
	warnCloser  io.Closer
	errCloser   io.Closer
	eventCloser io.Closer
)

func initLogger(logger **log.Logger, oldLogCloser *io.Closer, newLogWriter io.WriteCloser, logPrefix string, logFlags int) {
	if newLogWriter == nil {
		*logger = nil
		if *oldLogCloser != nil {
			(*oldLogCloser).Close()
			*oldLogCloser = nil
		}
		return
	}

	if *logger != nil {
		(*logger).SetOutput(newLogWriter)
	} else {
		*logger = log.New(newLogWriter, logPrefix, logFlags)
	}

	if *oldLogCloser != nil {
		(*oldLogCloser).Close()
	}
	*oldLogCloser = newLogWriter
}

// Init initailizes the logs with the given io.WriteClosers. If `Init` was previously called, existing loggers are Closed. If you have loggers which are not Closers or which must not be Closed, wrap them with `log.NopCloser`.
func Init(eventW, errW, warnW, infoW, debugW io.WriteCloser) {
	initLogger(&Debug, &debugCloser, debugW, "DEBUG: ", log.Lshortfile)
	initLogger(&Info, &infoCloser, infoW, "INFO: ", log.Lshortfile)
	initLogger(&Warning, &warnCloser, warnW, "WARNING: ", log.Lshortfile)
	initLogger(&Error, &errCloser, errW, "ERROR: ", log.Lshortfile)
	initLogger(&Event, &eventCloser, eventW, "", 0)
}

const timeFormat = time.RFC3339Nano
const stackFrame = 3

func Errorf(format string, v ...interface{}) {
	if Error == nil {
		return
	}
	Error.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Errorln(v ...interface{}) {
	if Error == nil {
		return
	}
	Error.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}
func Warnf(format string, v ...interface{}) {
	if Warning == nil {
		return
	}
	Warning.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Warnln(v ...interface{}) {
	if Warning == nil {
		return
	}
	Warning.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}
func Infof(format string, v ...interface{}) {
	if Info == nil {
		return
	}
	Info.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Infoln(v ...interface{}) {
	if Info == nil {
		return
	}
	Info.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}
func Debugf(format string, v ...interface{}) {
	if Debug == nil {
		return
	}
	Debug.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Debugln(v ...interface{}) {
	if Debug == nil {
		return
	}
	Debug.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}

// event log entries (TM event.log, TR access.log, etc)
func Eventf(t time.Time, format string, v ...interface{}) {
	if Event == nil {
		return
	}
	// 1484001185.287 ...
	Event.Printf("%.3f %s", float64(t.Unix())+(float64(t.Nanosecond())/1e9), fmt.Sprintf(format, v...))
}

// EventfRaw writes to the event log with no prefix.
func EventfRaw(format string, v ...interface{}) {
	Event.Printf(format, v...)
}

// Close calls `Close()` on the given Closer, and logs any error. On error, the context is logged, followed by a colon, the error message, and a newline. This is primarily designed to be used in `defer`, for example, `defer log.Close(resp.Body, "readData fetching /foo/bar")`.
func Close(c io.Closer, context string) {
	err := c.Close()
	if err != nil {
		Errorf("%v: %v", context, err)
	}
}

// Closef acts like Close, with a given format string and values, followed by a colon, the error message, and a newline. The given values are not coerced, concatenated, or printed unless an error occurs, so this is more efficient than `Close()`.
func Closef(c io.Closer, contextFormat string, v ...interface{}) {
	err := c.Close()
	if err != nil {
		Errorf(contextFormat, v...)
		Errorf(": %v", err)
	}
}

// Write calls `Write()` on the given Writer, and logs any error. On error, the context is logged, followed by a colon, the error message, and a newline.
func Write(w io.Writer, b []byte, context string) {
	_, err := w.Write(b)
	if err != nil {
		Errorf("%v: %v", context, err)
	}
}

// Writef acts like Write, with a given format string and values, followed by a colon, the error message, and a newline. The given values are not coerced, concatenated, or printed unless an error occurs, so this is more efficient than `Write()`.
func Writef(w io.Writer, b []byte, contextFormat string, v ...interface{}) {
	_, err := w.Write(b)
	if err != nil {
		Errorf(contextFormat, v...)
		Errorf(": %v", err)
	}
}

type nopCloser struct {
	io.Writer
}

func (nopCloser) Close() error { return nil }

func NopCloser(w io.Writer) io.WriteCloser {
	return nopCloser{w}
}

// LogLocation is a location to log to. This may be stdout, stderr, null (/dev/null), or a valid file path.
type LogLocation string

const (
	// LogLocationStdout indicates the stdout IO stream
	LogLocationStdout = "stdout"
	// LogLocationStderr indicates the stderr IO stream
	LogLocationStderr = "stderr"
	// LogLocationNull indicates the null IO stream (/dev/null)
	LogLocationNull = "null"
	//StaticFileDir is the directory that contains static html and js files.
	StaticFileDir = "/opt/traffic_monitor/static/"
)

func GetLogWriter(location LogLocation) (io.WriteCloser, error) {
	switch location {
	case LogLocationStdout:
		return NopCloser(os.Stdout), nil
	case LogLocationStderr:
		return NopCloser(os.Stderr), nil
	case LogLocationNull:
		fallthrough
	case "":
		return nil, nil
	default:
		return os.OpenFile(string(location), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	}
}

type Config interface {
	ErrorLog() LogLocation
	WarningLog() LogLocation
	InfoLog() LogLocation
	DebugLog() LogLocation
	EventLog() LogLocation
}

func GetLogWriters(cfg Config) (io.WriteCloser, io.WriteCloser, io.WriteCloser, io.WriteCloser, io.WriteCloser, error) {
	eventLoc := cfg.EventLog()
	errLoc := cfg.ErrorLog()
	warnLoc := cfg.WarningLog()
	infoLoc := cfg.InfoLog()
	debugLoc := cfg.DebugLog()

	eventW, err := GetLogWriter(eventLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log event writer %v: %v", eventLoc, err)
	}
	errW, err := GetLogWriter(errLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log error writer %v: %v", errLoc, err)
	}
	warnW, err := GetLogWriter(warnLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log warning writer %v: %v", warnLoc, err)
	}
	infoW, err := GetLogWriter(infoLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log info writer %v: %v", infoLoc, err)
	}
	debugW, err := GetLogWriter(debugLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log debug writer %v: %v", debugLoc, err)
	}
	return eventW, errW, warnW, infoW, debugW, nil
}

func InitCfg(cfg Config) error {
	eventW, errW, warnW, infoW, debugW, err := GetLogWriters(cfg)
	if err != nil {
		return err
	}
	Init(eventW, errW, warnW, infoW, debugW)
	return nil
}
<commit_msg>Fix TM log EventfRaw for nil loggers<commit_after>// Inspired by https://www.goinggo.net/2013/11/using-log-package-in-go.html
package log

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"fmt"
	"io"
	"log"
	"os"
	"time"
)

var (
	Debug       *log.Logger
	Info        *log.Logger
	Warning     *log.Logger
	Error       *log.Logger
	Event       *log.Logger
	debugCloser io.Closer
	infoCloser  io.Closer
	warnCloser  io.Closer
	errCloser   io.Closer
	eventCloser io.Closer
)

func initLogger(logger **log.Logger, oldLogCloser *io.Closer, newLogWriter io.WriteCloser, logPrefix string, logFlags int) {
	if newLogWriter == nil {
		*logger = nil
		if *oldLogCloser != nil {
			(*oldLogCloser).Close()
			*oldLogCloser = nil
		}
		return
	}

	if *logger != nil {
		(*logger).SetOutput(newLogWriter)
	} else {
		*logger = log.New(newLogWriter, logPrefix, logFlags)
	}

	if *oldLogCloser != nil {
		(*oldLogCloser).Close()
	}
	*oldLogCloser = newLogWriter
}

// Init initailizes the logs with the given io.WriteClosers. If `Init` was previously called, existing loggers are Closed. If you have loggers which are not Closers or which must not be Closed, wrap them with `log.NopCloser`.
func Init(eventW, errW, warnW, infoW, debugW io.WriteCloser) {
	initLogger(&Debug, &debugCloser, debugW, "DEBUG: ", log.Lshortfile)
	initLogger(&Info, &infoCloser, infoW, "INFO: ", log.Lshortfile)
	initLogger(&Warning, &warnCloser, warnW, "WARNING: ", log.Lshortfile)
	initLogger(&Error, &errCloser, errW, "ERROR: ", log.Lshortfile)
	initLogger(&Event, &eventCloser, eventW, "", 0)
}

const timeFormat = time.RFC3339Nano
const stackFrame = 3

func Errorf(format string, v ...interface{}) {
	if Error == nil {
		return
	}
	Error.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Errorln(v ...interface{}) {
	if Error == nil {
		return
	}
	Error.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}
func Warnf(format string, v ...interface{}) {
	if Warning == nil {
		return
	}
	Warning.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Warnln(v ...interface{}) {
	if Warning == nil {
		return
	}
	Warning.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}
func Infof(format string, v ...interface{}) {
	if Info == nil {
		return
	}
	Info.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Infoln(v ...interface{}) {
	if Info == nil {
		return
	}
	Info.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}
func Debugf(format string, v ...interface{}) {
	if Debug == nil {
		return
	}
	Debug.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintf(format, v...))
}
func Debugln(v ...interface{}) {
	if Debug == nil {
		return
	}
	Debug.Output(stackFrame, time.Now().Format(timeFormat)+": "+fmt.Sprintln(v...))
}

// event log entries (TM event.log, TR access.log, etc)
func Eventf(t time.Time, format string, v ...interface{}) {
	if Event == nil {
		return
	}
	// 1484001185.287 ...
	Event.Printf("%.3f %s", float64(t.Unix())+(float64(t.Nanosecond())/1e9), fmt.Sprintf(format, v...))
}

// EventfRaw writes to the event log with no prefix.
func EventfRaw(format string, v ...interface{}) {
	if Event == nil {
		return
	}
	Event.Printf(format, v...)
}

// Close calls `Close()` on the given Closer, and logs any error. On error, the context is logged, followed by a colon, the error message, and a newline. This is primarily designed to be used in `defer`, for example, `defer log.Close(resp.Body, "readData fetching /foo/bar")`.
func Close(c io.Closer, context string) {
	err := c.Close()
	if err != nil {
		Errorf("%v: %v", context, err)
	}
}

// Closef acts like Close, with a given format string and values, followed by a colon, the error message, and a newline. The given values are not coerced, concatenated, or printed unless an error occurs, so this is more efficient than `Close()`.
func Closef(c io.Closer, contextFormat string, v ...interface{}) {
	err := c.Close()
	if err != nil {
		Errorf(contextFormat, v...)
		Errorf(": %v", err)
	}
}

// Write calls `Write()` on the given Writer, and logs any error. On error, the context is logged, followed by a colon, the error message, and a newline.
func Write(w io.Writer, b []byte, context string) {
	_, err := w.Write(b)
	if err != nil {
		Errorf("%v: %v", context, err)
	}
}

// Writef acts like Write, with a given format string and values, followed by a colon, the error message, and a newline. The given values are not coerced, concatenated, or printed unless an error occurs, so this is more efficient than `Write()`.
func Writef(w io.Writer, b []byte, contextFormat string, v ...interface{}) {
	_, err := w.Write(b)
	if err != nil {
		Errorf(contextFormat, v...)
		Errorf(": %v", err)
	}
}

type nopCloser struct {
	io.Writer
}

func (nopCloser) Close() error { return nil }

func NopCloser(w io.Writer) io.WriteCloser {
	return nopCloser{w}
}

// LogLocation is a location to log to. This may be stdout, stderr, null (/dev/null), or a valid file path.
type LogLocation string

const (
	// LogLocationStdout indicates the stdout IO stream
	LogLocationStdout = "stdout"
	// LogLocationStderr indicates the stderr IO stream
	LogLocationStderr = "stderr"
	// LogLocationNull indicates the null IO stream (/dev/null)
	LogLocationNull = "null"
	//StaticFileDir is the directory that contains static html and js files.
	StaticFileDir = "/opt/traffic_monitor/static/"
)

func GetLogWriter(location LogLocation) (io.WriteCloser, error) {
	switch location {
	case LogLocationStdout:
		return NopCloser(os.Stdout), nil
	case LogLocationStderr:
		return NopCloser(os.Stderr), nil
	case LogLocationNull:
		fallthrough
	case "":
		return nil, nil
	default:
		return os.OpenFile(string(location), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
	}
}

type Config interface {
	ErrorLog() LogLocation
	WarningLog() LogLocation
	InfoLog() LogLocation
	DebugLog() LogLocation
	EventLog() LogLocation
}

func GetLogWriters(cfg Config) (io.WriteCloser, io.WriteCloser, io.WriteCloser, io.WriteCloser, io.WriteCloser, error) {
	eventLoc := cfg.EventLog()
	errLoc := cfg.ErrorLog()
	warnLoc := cfg.WarningLog()
	infoLoc := cfg.InfoLog()
	debugLoc := cfg.DebugLog()

	eventW, err := GetLogWriter(eventLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log event writer %v: %v", eventLoc, err)
	}
	errW, err := GetLogWriter(errLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log error writer %v: %v", errLoc, err)
	}
	warnW, err := GetLogWriter(warnLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log warning writer %v: %v", warnLoc, err)
	}
	infoW, err := GetLogWriter(infoLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log info writer %v: %v", infoLoc, err)
	}
	debugW, err := GetLogWriter(debugLoc)
	if err != nil {
		return nil, nil, nil, nil, nil, fmt.Errorf("getting log debug writer %v: %v", debugLoc, err)
	}
	return eventW, errW, warnW, infoW, debugW, nil
}

func InitCfg(cfg Config) error {
	eventW, errW, warnW, infoW, debugW, err := GetLogWriters(cfg)
	if err != nil {
		return err
	}
	Init(eventW, errW, warnW, infoW, debugW)
	return nil
}
<|endoftext|>


<commit_before>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"crypto/tls"
	"database/sql"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"time"

	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
)

// Routes returns the routes, and a catchall route for when no route matches.
func Routes(d ServerData) ([]Route, http.Handler, error) {
	rd, err := routeData(d)
	if err != nil {
		return nil, nil, err
	}
	return []Route{
		{1.2, http.MethodGet, "cdns/{cdn}/configs/monitoring$", wrapHeaders(wrapAuth(monitoringHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, MonitoringPrivLevel))},
		{1.2, http.MethodGet, "cdns/{cdn}/configs/monitoring.json$", wrapHeaders(wrapAuth(monitoringHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, MonitoringPrivLevel))},
		{1.2, http.MethodGet, "servers-wip$", wrapHeaders(wrapAuthWithData(serversHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "servers-wip.json$", wrapHeaders(wrapAuthWithData(serversHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "cdns-wip$", wrapHeaders(wrapAuthWithData(cdnsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, CdnsPrivLevel))},
		{1.2, http.MethodGet, "cdns-wip.json$", wrapHeaders(wrapAuthWithData(cdnsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, CdnsPrivLevel))},
		{1.2, http.MethodPost, "servers/{server}/deliveryservices$", wrapHeaders(wrapAuthWithData(assignDeliveryServicesToServerHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, PrivLevelOperations))},
	}, rootHandler(d), nil
}

type RouteData struct {
	PrivLevelStmt *sql.Stmt
}

func routeData(d ServerData) (RouteData, error) {
	rd := RouteData{}
	err := error(nil)

	if rd.PrivLevelStmt, err = preparePrivLevelStmt(d.DB); err != nil {
		return rd, fmt.Errorf("Error preparing db priv level query: ", err)
	}

	return rd, nil
}

// getRootHandler returns the / handler for the service, which reverse-proxies the old Perl Traffic Ops
func rootHandler(d ServerData) http.Handler {
	// debug
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		DialContext:     (&net.Dialer{
			Timeout: time.Duration(d.Config.ProxyTimeout) * time.Second,
			KeepAlive: time.Duration(d.Config.ProxyKeepAlive) * time.Second,
		}).DialContext,
		TLSHandshakeTimeout: time.Duration(d.Config.ProxyTLSTimeout) * time.Second,
		ResponseHeaderTimeout: time.Duration(d.Config.ProxyReadHeaderTimeout) * time.Second,
		//Other knobs we can turn: ExpectContinueTimeout,IdleConnTimeout
	}
	rp := httputil.NewSingleHostReverseProxy(d.TOURL)
	rp.Transport = tr

	rp.ErrorLog = log.Error //if we don't provide a logger to the reverse proxy it logs to stdout/err and is lost when ran by a script.
	log.Debugf("our reverseProxy: %++v\n",rp)
	log.Debugf("our reverseProxy's transport: %++v\n", tr)
	loggingProxyHandler := wrapAccessLog(d.TOSecret, rp)
	return loggingProxyHandler
}
<commit_msg>hooked in the route for regions<commit_after>package main

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import (
	"crypto/tls"
	"database/sql"
	"fmt"
	"net"
	"net/http"
	"net/http/httputil"
	"time"

	"github.com/apache/incubator-trafficcontrol/traffic_monitor_golang/common/log"
)

// Routes returns the routes, and a catchall route for when no route matches.
func Routes(d ServerData) ([]Route, http.Handler, error) {
	rd, err := routeData(d)
	if err != nil {
		return nil, nil, err
	}
	return []Route{
		{1.2, http.MethodGet, "cdns/{cdn}/configs/monitoring$", wrapHeaders(wrapAuth(monitoringHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, MonitoringPrivLevel))},
		{1.2, http.MethodGet, "cdns/{cdn}/configs/monitoring.json$", wrapHeaders(wrapAuth(monitoringHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, MonitoringPrivLevel))},
		{1.2, http.MethodGet, "regions-wip/{id}$", wrapHeaders(wrapAuthWithData(regionsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "regions-wip.json$", wrapHeaders(wrapAuthWithData(regionsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "regions-wip$", wrapHeaders(wrapAuthWithData(regionsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "regions-wip.json$", wrapHeaders(wrapAuthWithData(serversHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "servers-wip.json$", wrapHeaders(wrapAuthWithData(serversHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "servers-wip$", wrapHeaders(wrapAuthWithData(serversHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "servers-wip.json$", wrapHeaders(wrapAuthWithData(serversHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, ServersPrivLevel))},
		{1.2, http.MethodGet, "cdns-wip$", wrapHeaders(wrapAuthWithData(cdnsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, CdnsPrivLevel))},
		{1.2, http.MethodGet, "cdns-wip.json$", wrapHeaders(wrapAuthWithData(cdnsHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, CdnsPrivLevel))},
		{1.2, http.MethodPost, "servers/{server}/deliveryservices$", wrapHeaders(wrapAuthWithData(assignDeliveryServicesToServerHandler(d.DB), d.Insecure, d.TOSecret, rd.PrivLevelStmt, PrivLevelOperations))},
	}, rootHandler(d), nil
}

type RouteData struct {
	PrivLevelStmt *sql.Stmt
}

func routeData(d ServerData) (RouteData, error) {
	rd := RouteData{}
	err := error(nil)

	if rd.PrivLevelStmt, err = preparePrivLevelStmt(d.DB); err != nil {
		return rd, fmt.Errorf("Error preparing db priv level query: ", err)
	}

	return rd, nil
}

// getRootHandler returns the / handler for the service, which reverse-proxies the old Perl Traffic Ops
func rootHandler(d ServerData) http.Handler {
	// debug
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		DialContext:     (&net.Dialer{
			Timeout: time.Duration(d.Config.ProxyTimeout) * time.Second,
			KeepAlive: time.Duration(d.Config.ProxyKeepAlive) * time.Second,
		}).DialContext,
		TLSHandshakeTimeout: time.Duration(d.Config.ProxyTLSTimeout) * time.Second,
		ResponseHeaderTimeout: time.Duration(d.Config.ProxyReadHeaderTimeout) * time.Second,
		//Other knobs we can turn: ExpectContinueTimeout,IdleConnTimeout
	}
	rp := httputil.NewSingleHostReverseProxy(d.TOURL)
	rp.Transport = tr

	rp.ErrorLog = log.Error //if we don't provide a logger to the reverse proxy it logs to stdout/err and is lost when ran by a script.
	log.Debugf("our reverseProxy: %++v\n",rp)
	log.Debugf("our reverseProxy's transport: %++v\n", tr)
	loggingProxyHandler := wrapAccessLog(d.TOSecret, rp)
	return loggingProxyHandler
}
<|endoftext|>


<commit_before>package main

// These can be changed using compile time flags. e.g.,
//
// go build -ldflags "-X main.defaultConfigFile=/usr/local/etc/GeoIP.conf
// -X main.defaultDatabaseDirectory=/usr/local/share/GeoIP"
var (
	// I'm not sure these make sense. However they can be overridden at runtime
	// and in the configuration, so we have some flexibility.
	defaultConfigFile        = `C:\ProgramData\GeoIP.conf`
	defaultDatabaseDirectory = `C:\ProgramData\GeoIP`
)
<commit_msg>Get Windows drive from environment<commit_after>package main

import (
	"os"
)

// These can be changed using compile time flags. e.g.,
//
// go build -ldflags "-X main.defaultConfigFile=/usr/local/etc/GeoIP.conf
// -X main.defaultDatabaseDirectory=/usr/local/share/GeoIP"
var (
	// I'm not sure these make sense. However they can be overridden at runtime
	// and in the configuration, so we have some flexibility.
	defaultConfigFile        = os.Getenv("SYSTEMDRIVE") + `\ProgramData\GeoIP.conf`
	defaultDatabaseDirectory = os.Getenv("SYSTEMDRIVE") + `\ProgramData\MaxMind\GeoIP`
)
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package upgrade

import (
	"os"

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

	"k8s.io/apimachinery/pkg/util/sets"
	clientset "k8s.io/client-go/kubernetes"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
	"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
	phases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade/node"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
	configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
)

// nodeOptions defines all the options exposed via flags by kubeadm upgrade node.
// Please note that this structure includes the public kubeadm config API, but only a subset of the options
// supported by this api will be exposed as a flag.
type nodeOptions struct {
	kubeConfigPath        string
	etcdUpgrade           bool
	renewCerts            bool
	dryRun                bool
	patchesDir            string
	ignorePreflightErrors []string
}

// compile-time assert that the local data object satisfies the phases data interface.
var _ phases.Data = &nodeData{}

// nodeData defines all the runtime information used when running the kubeadm upgrade node worklow;
// this data is shared across all the phases that are included in the workflow.
type nodeData struct {
	etcdUpgrade           bool
	renewCerts            bool
	dryRun                bool
	cfg                   *kubeadmapi.InitConfiguration
	isControlPlaneNode    bool
	client                clientset.Interface
	patchesDir            string
	ignorePreflightErrors sets.String
}

// newCmdNode returns the cobra command for `kubeadm upgrade node`
func newCmdNode() *cobra.Command {
	nodeOptions := newNodeOptions()
	nodeRunner := workflow.NewRunner()

	cmd := &cobra.Command{
		Use:   "node",
		Short: "Upgrade commands for a node in the cluster",
		RunE: func(cmd *cobra.Command, args []string) error {
			return nodeRunner.Run(args)
		},
		Args: cobra.NoArgs,
	}

	// adds flags to the node command
	// flags could be eventually inherited by the sub-commands automatically generated for phases
	addUpgradeNodeFlags(cmd.Flags(), nodeOptions)
	options.AddPatchesFlag(cmd.Flags(), &nodeOptions.patchesDir)

	// initialize the workflow runner with the list of phases
	nodeRunner.AppendPhase(phases.NewPreflightPhase())
	nodeRunner.AppendPhase(phases.NewControlPlane())
	nodeRunner.AppendPhase(phases.NewKubeletConfigPhase())

	// sets the data builder function, that will be used by the runner
	// both when running the entire workflow or single phases
	nodeRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) {
		return newNodeData(cmd, args, nodeOptions)
	})

	// binds the Runner to kubeadm upgrade node command by altering
	// command help, adding --skip-phases flag and by adding phases subcommands
	nodeRunner.BindToCommand(cmd)

	return cmd
}

// newNodeOptions returns a struct ready for being used for creating cmd kubeadm upgrade node flags.
func newNodeOptions() *nodeOptions {
	return &nodeOptions{
		kubeConfigPath: constants.GetKubeletKubeConfigPath(),
		dryRun:         false,
		renewCerts:     true,
		etcdUpgrade:    true,
	}
}

func addUpgradeNodeFlags(flagSet *flag.FlagSet, nodeOptions *nodeOptions) {
	options.AddKubeConfigFlag(flagSet, &nodeOptions.kubeConfigPath)
	flagSet.BoolVar(&nodeOptions.dryRun, options.DryRun, nodeOptions.dryRun, "Do not change any state, just output the actions that would be performed.")
	flagSet.BoolVar(&nodeOptions.renewCerts, options.CertificateRenewal, nodeOptions.renewCerts, "Perform the renewal of certificates used by component changed during upgrades.")
	flagSet.BoolVar(&nodeOptions.etcdUpgrade, options.EtcdUpgrade, nodeOptions.etcdUpgrade, "Perform the upgrade of etcd.")
	flagSet.StringSliceVar(&nodeOptions.ignorePreflightErrors, options.IgnorePreflightErrors, nodeOptions.ignorePreflightErrors, "A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.")
}

// newNodeData returns a new nodeData struct to be used for the execution of the kubeadm upgrade node workflow.
// This func takes care of validating nodeOptions passed to the command, and then it converts
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm upgrade node workflow
func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*nodeData, error) {
	client, err := getClient(options.kubeConfigPath, options.dryRun)
	if err != nil {
		return nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", options.kubeConfigPath)
	}

	// isControlPlane checks if a node is a control-plane node by looking up
	// the kube-apiserver manifest file
	isControlPlaneNode := true
	filepath := constants.GetStaticPodFilepath(constants.KubeAPIServer, constants.GetStaticPodDirectory())
	if _, err := os.Stat(filepath); os.IsNotExist(err) {
		isControlPlaneNode = false
	}

	// Fetches the cluster configuration
	// NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node
	//    (worker node), we are not reading local API address and the CRI socket from the node object
	cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "upgrade", !isControlPlaneNode, false)
	if err != nil {
		return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
	}

	ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
	if err != nil {
		return nil, err
	}
	// Also set the union of pre-flight errors to JoinConfiguration, to provide a consistent view of the runtime configuration:
	cfg.NodeRegistration.IgnorePreflightErrors = ignorePreflightErrorsSet.List()

	return &nodeData{
		etcdUpgrade:           options.etcdUpgrade,
		renewCerts:            options.renewCerts,
		dryRun:                options.dryRun,
		cfg:                   cfg,
		client:                client,
		isControlPlaneNode:    isControlPlaneNode,
		patchesDir:            options.patchesDir,
		ignorePreflightErrors: ignorePreflightErrorsSet,
	}, nil
}

// DryRun returns the dryRun flag.
func (d *nodeData) DryRun() bool {
	return d.dryRun
}

// EtcdUpgrade returns the etcdUpgrade flag.
func (d *nodeData) EtcdUpgrade() bool {
	return d.etcdUpgrade
}

// RenewCerts returns the renewCerts flag.
func (d *nodeData) RenewCerts() bool {
	return d.renewCerts
}

// Cfg returns initConfiguration.
func (d *nodeData) Cfg() *kubeadmapi.InitConfiguration {
	return d.cfg
}

// IsControlPlaneNode returns the isControlPlaneNode flag.
func (d *nodeData) IsControlPlaneNode() bool {
	return d.isControlPlaneNode
}

// Client returns a Kubernetes client to be used by kubeadm.
func (d *nodeData) Client() clientset.Interface {
	return d.client
}

// PatchesDir returns the folder where patches for components are stored
func (d *nodeData) PatchesDir() string {
	return d.patchesDir
}

// IgnorePreflightErrors returns the list of preflight errors to ignore.
func (d *nodeData) IgnorePreflightErrors() sets.String {
	return d.ignorePreflightErrors
}
<commit_msg>Fix Spelling error  about [workflow] for kubeadm<commit_after>/*
Copyright 2018 The Kubernetes Authors.

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

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

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

package upgrade

import (
	"os"

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

	"k8s.io/apimachinery/pkg/util/sets"
	clientset "k8s.io/client-go/kubernetes"
	kubeadmapi "k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm"
	"k8s.io/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/options"
	phases "k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/upgrade/node"
	"k8s.io/kubernetes/cmd/kubeadm/app/cmd/phases/workflow"
	"k8s.io/kubernetes/cmd/kubeadm/app/constants"
	configutil "k8s.io/kubernetes/cmd/kubeadm/app/util/config"
)

// nodeOptions defines all the options exposed via flags by kubeadm upgrade node.
// Please note that this structure includes the public kubeadm config API, but only a subset of the options
// supported by this api will be exposed as a flag.
type nodeOptions struct {
	kubeConfigPath        string
	etcdUpgrade           bool
	renewCerts            bool
	dryRun                bool
	patchesDir            string
	ignorePreflightErrors []string
}

// compile-time assert that the local data object satisfies the phases data interface.
var _ phases.Data = &nodeData{}

// nodeData defines all the runtime information used when running the kubeadm upgrade node workflow;
// this data is shared across all the phases that are included in the workflow.
type nodeData struct {
	etcdUpgrade           bool
	renewCerts            bool
	dryRun                bool
	cfg                   *kubeadmapi.InitConfiguration
	isControlPlaneNode    bool
	client                clientset.Interface
	patchesDir            string
	ignorePreflightErrors sets.String
}

// newCmdNode returns the cobra command for `kubeadm upgrade node`
func newCmdNode() *cobra.Command {
	nodeOptions := newNodeOptions()
	nodeRunner := workflow.NewRunner()

	cmd := &cobra.Command{
		Use:   "node",
		Short: "Upgrade commands for a node in the cluster",
		RunE: func(cmd *cobra.Command, args []string) error {
			return nodeRunner.Run(args)
		},
		Args: cobra.NoArgs,
	}

	// adds flags to the node command
	// flags could be eventually inherited by the sub-commands automatically generated for phases
	addUpgradeNodeFlags(cmd.Flags(), nodeOptions)
	options.AddPatchesFlag(cmd.Flags(), &nodeOptions.patchesDir)

	// initialize the workflow runner with the list of phases
	nodeRunner.AppendPhase(phases.NewPreflightPhase())
	nodeRunner.AppendPhase(phases.NewControlPlane())
	nodeRunner.AppendPhase(phases.NewKubeletConfigPhase())

	// sets the data builder function, that will be used by the runner
	// both when running the entire workflow or single phases
	nodeRunner.SetDataInitializer(func(cmd *cobra.Command, args []string) (workflow.RunData, error) {
		return newNodeData(cmd, args, nodeOptions)
	})

	// binds the Runner to kubeadm upgrade node command by altering
	// command help, adding --skip-phases flag and by adding phases subcommands
	nodeRunner.BindToCommand(cmd)

	return cmd
}

// newNodeOptions returns a struct ready for being used for creating cmd kubeadm upgrade node flags.
func newNodeOptions() *nodeOptions {
	return &nodeOptions{
		kubeConfigPath: constants.GetKubeletKubeConfigPath(),
		dryRun:         false,
		renewCerts:     true,
		etcdUpgrade:    true,
	}
}

func addUpgradeNodeFlags(flagSet *flag.FlagSet, nodeOptions *nodeOptions) {
	options.AddKubeConfigFlag(flagSet, &nodeOptions.kubeConfigPath)
	flagSet.BoolVar(&nodeOptions.dryRun, options.DryRun, nodeOptions.dryRun, "Do not change any state, just output the actions that would be performed.")
	flagSet.BoolVar(&nodeOptions.renewCerts, options.CertificateRenewal, nodeOptions.renewCerts, "Perform the renewal of certificates used by component changed during upgrades.")
	flagSet.BoolVar(&nodeOptions.etcdUpgrade, options.EtcdUpgrade, nodeOptions.etcdUpgrade, "Perform the upgrade of etcd.")
	flagSet.StringSliceVar(&nodeOptions.ignorePreflightErrors, options.IgnorePreflightErrors, nodeOptions.ignorePreflightErrors, "A list of checks whose errors will be shown as warnings. Example: 'IsPrivilegedUser,Swap'. Value 'all' ignores errors from all checks.")
}

// newNodeData returns a new nodeData struct to be used for the execution of the kubeadm upgrade node workflow.
// This func takes care of validating nodeOptions passed to the command, and then it converts
// options into the internal InitConfiguration type that is used as input all the phases in the kubeadm upgrade node workflow
func newNodeData(cmd *cobra.Command, args []string, options *nodeOptions) (*nodeData, error) {
	client, err := getClient(options.kubeConfigPath, options.dryRun)
	if err != nil {
		return nil, errors.Wrapf(err, "couldn't create a Kubernetes client from file %q", options.kubeConfigPath)
	}

	// isControlPlane checks if a node is a control-plane node by looking up
	// the kube-apiserver manifest file
	isControlPlaneNode := true
	filepath := constants.GetStaticPodFilepath(constants.KubeAPIServer, constants.GetStaticPodDirectory())
	if _, err := os.Stat(filepath); os.IsNotExist(err) {
		isControlPlaneNode = false
	}

	// Fetches the cluster configuration
	// NB in case of control-plane node, we are reading all the info for the node; in case of NOT control-plane node
	//    (worker node), we are not reading local API address and the CRI socket from the node object
	cfg, err := configutil.FetchInitConfigurationFromCluster(client, os.Stdout, "upgrade", !isControlPlaneNode, false)
	if err != nil {
		return nil, errors.Wrap(err, "unable to fetch the kubeadm-config ConfigMap")
	}

	ignorePreflightErrorsSet, err := validation.ValidateIgnorePreflightErrors(options.ignorePreflightErrors, cfg.NodeRegistration.IgnorePreflightErrors)
	if err != nil {
		return nil, err
	}
	// Also set the union of pre-flight errors to JoinConfiguration, to provide a consistent view of the runtime configuration:
	cfg.NodeRegistration.IgnorePreflightErrors = ignorePreflightErrorsSet.List()

	return &nodeData{
		etcdUpgrade:           options.etcdUpgrade,
		renewCerts:            options.renewCerts,
		dryRun:                options.dryRun,
		cfg:                   cfg,
		client:                client,
		isControlPlaneNode:    isControlPlaneNode,
		patchesDir:            options.patchesDir,
		ignorePreflightErrors: ignorePreflightErrorsSet,
	}, nil
}

// DryRun returns the dryRun flag.
func (d *nodeData) DryRun() bool {
	return d.dryRun
}

// EtcdUpgrade returns the etcdUpgrade flag.
func (d *nodeData) EtcdUpgrade() bool {
	return d.etcdUpgrade
}

// RenewCerts returns the renewCerts flag.
func (d *nodeData) RenewCerts() bool {
	return d.renewCerts
}

// Cfg returns initConfiguration.
func (d *nodeData) Cfg() *kubeadmapi.InitConfiguration {
	return d.cfg
}

// IsControlPlaneNode returns the isControlPlaneNode flag.
func (d *nodeData) IsControlPlaneNode() bool {
	return d.isControlPlaneNode
}

// Client returns a Kubernetes client to be used by kubeadm.
func (d *nodeData) Client() clientset.Interface {
	return d.client
}

// PatchesDir returns the folder where patches for components are stored
func (d *nodeData) PatchesDir() string {
	return d.patchesDir
}

// IgnorePreflightErrors returns the list of preflight errors to ignore.
func (d *nodeData) IgnorePreflightErrors() sets.String {
	return d.ignorePreflightErrors
}
<|endoftext|>


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

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"text/template"

	"launchpad.net/gnuflag"
	"launchpad.net/loggo"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/bootstrap"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/configstore"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	_ "launchpad.net/juju-core/provider/all"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/utils"
)

func main() {
	Main(os.Args)
}

func Main(args []string) {
	if err := juju.InitJujuHome(); err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err)
		os.Exit(2)
	}
	os.Exit(cmd.Main(&restoreCommand{}, cmd.DefaultContext(), args[1:]))
}

var logger = loggo.GetLogger("juju.plugins.restore")

const restoreDoc = `
Restore restores a backup created with juju backup
by creating a new juju bootstrap instance and arranging
it so that the existing instances in the environment
talk to it.

It verifies that the existing bootstrap instance is
not running. The given constraints will be used
to choose the new instance.
`

type restoreCommand struct {
	cmd.EnvCommandBase
	Log         cmd.Log
	Constraints constraints.Value
	backupFile  string
}

func (c *restoreCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "juju-restore",
		Purpose: "Restore a backup made with juju backup",
		Args:    "<backupfile.tar.gz>",
		Doc:     restoreDoc,
	}
}

func (c *restoreCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set environment constraints")
	c.Log.AddFlags(f)
}

func (c *restoreCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no backup file specified")
	}
	c.backupFile = args[0]
	return nil
}

var updateBootstrapMachineTemplate = mustParseTemplate(`
	set -e -x
	tar xzf juju-backup.tgz
	test -d juju-backup

	initctl stop jujud-machine-0

	initctl stop juju-db
	rm -r /var/lib/juju /var/log/juju
	tar -C / -xvp -f juju-backup/root.tar
	mkdir -p /var/lib/juju/db
	mongorestore --drop --dbpath /var/lib/juju/db juju-backup/dump
	initctl start juju-db

	mongoEval() {
		mongo --ssl -u admin -p {{.AdminSecret | shquote}} localhost:37017/admin --eval "$1"
	}
	# wait for mongo to come up after starting the juju-db upstart service.
	for i in $(seq 1 60)
	do
		mongoEval ' ' && break
		sleep 2
	done
	mongoEval '
		db.machines.update({_id: 0}, {$set: {instanceid: '{{.NewInstanceId | printf "%q" | shquote}}' } })
		db.instanceData.update({_id: 0}, {$set: {instanceid: '{{.NewInstanceId | printf "%q"| shquote}}' } })
	'
	initctl start jujud-machine-0
`)

func updateBootstrapMachineScript(instanceId instance.Id, adminSecret string) string {
	return execTemplate(updateBootstrapMachineTemplate, struct {
		NewInstanceId instance.Id
		AdminSecret string
	}{instanceId, adminSecret})
}

func (c *restoreCommand) Run(ctx *cmd.Context) error {
	if err := c.Log.Start(ctx); err != nil {
		return err
	}
	store, err := configstore.Default()
	if err != nil {
		return err
	}
	cfg, _, err := environs.ConfigForName(c.EnvName, store)
	if err != nil {
		return err
	}
	env, err := rebootstrap(cfg, c.Constraints)
	if err != nil {
		return fmt.Errorf("cannot re-bootstrap environment: %v", err)
	}
	logger.Infof("connecting to newly bootstrapped instance")
	conn, err := juju.NewAPIConn(env, api.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot connect to bootstrap instance: %v", err)
	}
	newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile)
	if err != nil {
		return fmt.Errorf("cannot restore bootstrap machine: %v", err)
	}
	// Update the environ state to point to the new instance.
	if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{
		StateInstances: []instance.Id{newInstId},
	}); err != nil {
		return fmt.Errorf("cannot update environ bootstrap state storage: %v", err)
	}

	// Construct our own state info rather than using juju.NewConn so
	// that we can avoid storage eventual consistency issues
	// (and it's faster too).
	caCert, ok := cfg.CACert()
	if !ok {
		return fmt.Errorf("configuration has no CA certificate")
	}
	st, err := state.Open(&state.Info{
		Addrs:  []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())},
		CACert: caCert,
	}, state.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot open state: %v", err)
	}
	if err := updateAllMachines(st, machine0Addr); err != nil {
		return fmt.Errorf("cannot update machines: %v", err)
	}
	return nil
}

func rebootstrap(cfg *config.Config, cons constraints.Value) (environs.Environ, error) {
	logger.Infof("re-bootstrapping environment")
	// Turn on safe mode so that the newly bootstrapped instance
	// will not destroy all the instances it does not know about.
	cfg, err := cfg.Apply(map[string]interface{}{
		"provisioner-safe-mode": true,
	})
	if err != nil {
		return nil, fmt.Errorf("cannot enable provisioner-safe-mode: %v", err)
	}
	env, err := environs.New(cfg)
	if err != nil {
		return nil, err
	}
	state, err := bootstrap.LoadState(env.Storage())
	if err != nil {
		return nil, fmt.Errorf("cannot retrieve environment storage; perhaps the environment was not bootstrapped: %v", err)
	}
	if len(state.StateInstances) == 0 {
		return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped", err)
	}
	if len(state.StateInstances) > 1 {
		return nil, fmt.Errorf("restore does not support HA juju configurations yet")
	}
	inst, err := env.Instances(state.StateInstances)
	if err == nil {
		return nil, fmt.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
	}
	if err != environs.ErrNoInstances {
		return nil, fmt.Errorf("cannot detect whether old instance is still running: %v", err)
	}
	// Remove the storage so that we can bootstrap without the provider complaining.
	if err := env.Storage().Remove(bootstrap.StateFile); err != nil {
		return nil, fmt.Errorf("cannot remove %q from storage: %v", bootstrap.StateFile, err)
	}

	// TODO If we fail beyond here, then we won't have a state file and
	// we won't be able to re-run this script because it fails without it.
	// We could either try to recreate the file if we fail (which is itself
	// error-prone) or we could provide a --no-check flag to make
	// it go ahead anyway without the check.

	if err := bootstrap.Bootstrap(env, cons); err != nil {
		return nil, fmt.Errorf("cannot bootstrap new instance: %v", err)
	}
	return env, nil
}

func restoreBootstrapMachine(conn *juju.APIConn, backupFile string) (newInstId instance.Id, addr string, err error) {
	addr, err = conn.State.Client().PublicAddress("0")
	if err != nil {
		return "", "", fmt.Errorf("cannot get public address of bootstrap machine: %v", err)
	}
	status, err := conn.State.Client().Status()
	if err != nil {
		return "", "", fmt.Errorf("cannot get environment status: %v", err)
	}
	info, ok := status.Machines["0"]
	if !ok {
		return "", "", fmt.Errorf("cannot find bootstrap machine in status")
	}
	newInstId = instance.Id(info.InstanceId)

	if err := scp(backupFile, addr, "~/juju-backup.tgz"); err != nil {
		return "", "", fmt.Errorf("cannot copy backup file to bootstrap instance: %v", err)
	}

	adminSecret := conn.Environ.Config().AdminSecret()
	if err := ssh(addr, updateBootstrapMachineScript(newInstId, adminSecret)); err != nil {
		return "", "", fmt.Errorf("update script failed: %v", err)
	}
	return newInstId, addr, nil
}

var agentAddressTemplate = mustParseTemplate(`
set -exu
cd /var/lib/juju/agents
for agent in *
do
	initctl stop jujud-$agent
	sed -i.old -r "/^(stateaddresses|apiaddresses):/{
		n
		s/- .*(:[0-9]+)/- {{.Address}}\1/
	}" $agent/agent.conf
	if [[ $agent = unit-* ]]
	then
 		sed -i -r 's/change-version: [0-9]+$/change-version: 0/' $agent/state/relations/*/* || true
	fi
	initctl start jujud-$agent
done
sed -i -r 's/^(:syslogtag, startswith, "juju-" @)(.*)(:[0-9]+.*)$/\1{{.Address}}\3/' /etc/rsyslog.d/*-juju*.conf
`)

// setAgentAddressScript generates an ssh script argument to update state addresses
func setAgentAddressScript(stateAddr string) string {
	return execTemplate(agentAddressTemplate, struct {
		Address string
	}{stateAddr})
}

// updateAllMachines finds all machines and resets the stored state address
// in each of them. The address does not include the port.
func updateAllMachines(st *state.State, stateAddr string) error {
	machines, err := st.AllMachines()
	if err != nil {
		return err
	}
	pendingMachineCount := 0
	done := make(chan error)
	for _, machine := range machines {
		// A newly resumed state server requires no updating, and more
		// than one state server is not yet support by this plugin.
		if machine.IsManager() || machine.Life() == state.Dead {
			continue
		}
		pendingMachineCount++
		machine := machine
		go func() {
			err := runMachineUpdate(machine, setAgentAddressScript(stateAddr))
			if err != nil {
				logger.Errorf("failed to update machine %s: %v", machine, err)
			} else {
				logger.Infof("updated machine %s", machine)
			}
			done <- err
		}()
	}
	err = nil
	for ; pendingMachineCount > 0; pendingMachineCount-- {
		if updateErr := <-done; updateErr != nil && err == nil {
			err = fmt.Errorf("machine update failed")
		}
	}
	return err
}

// runMachineUpdate connects via ssh to the machine and runs the update script
func runMachineUpdate(m *state.Machine, sshArg string) error {
	logger.Infof("updating machine: %v\n", m)
	addr := instance.SelectPublicAddress(m.Addresses())
	if addr == "" {
		return fmt.Errorf("no appropriate public address found")
	}
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		sshArg,
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func ssh(addr string, script string) error {
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		"sudo -n bash -c " + utils.ShQuote(script),
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func scp(file, host, destFile string) error {
	cmd := exec.Command("scp", "-B", "-q", file, "ubuntu@"+host+":"+destFile)
	logger.Infof("copying backup file to bootstrap host")
	logger.Debugf("scp command: %s %q", cmd.Path, cmd.Args)
	out, err := cmd.CombinedOutput()
	if err == nil {
		return nil
	}
	if _, ok := err.(*exec.ExitError); ok {
		return fmt.Errorf("scp failed: %s", out)
	}
	return err
}

func mustParseTemplate(templ string) *template.Template {
	t := template.New("").Funcs(template.FuncMap{
		"shquote": utils.ShQuote,
	})
	return template.Must(t.Parse(templ))
}

func execTemplate(tmpl *template.Template, data interface{}) string {
	var buf bytes.Buffer
	err := updateBootstrapMachineTemplate.Execute(&buf, data)
	if err != nil {
		panic(fmt.Errorf("template error: %v", err))
	}
	return buf.String()
}
<commit_msg>juju-restore: better logging<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"text/template"

	"launchpad.net/gnuflag"
	"launchpad.net/loggo"

	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/constraints"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/bootstrap"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/environs/configstore"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/juju"
	_ "launchpad.net/juju-core/provider/all"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api"
	"launchpad.net/juju-core/utils"
)

func main() {
	Main(os.Args)
}

func Main(args []string) {
	if err := juju.InitJujuHome(); err != nil {
		fmt.Fprintf(os.Stderr, "error: %s\n", err)
		os.Exit(2)
	}
	os.Exit(cmd.Main(&restoreCommand{}, cmd.DefaultContext(), args[1:]))
}

var logger = loggo.GetLogger("juju.plugins.restore")

const restoreDoc = `
Restore restores a backup created with juju backup
by creating a new juju bootstrap instance and arranging
it so that the existing instances in the environment
talk to it.

It verifies that the existing bootstrap instance is
not running. The given constraints will be used
to choose the new instance.
`

type restoreCommand struct {
	cmd.EnvCommandBase
	Log         cmd.Log
	Constraints constraints.Value
	backupFile  string
}

func (c *restoreCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "juju-restore",
		Purpose: "Restore a backup made with juju backup",
		Args:    "<backupfile.tar.gz>",
		Doc:     restoreDoc,
	}
}

func (c *restoreCommand) SetFlags(f *gnuflag.FlagSet) {
	c.EnvCommandBase.SetFlags(f)
	f.Var(constraints.ConstraintsValue{&c.Constraints}, "constraints", "set environment constraints")
	c.Log.AddFlags(f)
}

func (c *restoreCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no backup file specified")
	}
	c.backupFile = args[0]
	return nil
}

var updateBootstrapMachineTemplate = mustParseTemplate(`
	set -e -x
	tar xzf juju-backup.tgz
	test -d juju-backup

	initctl stop jujud-machine-0

	initctl stop juju-db
	rm -r /var/lib/juju /var/log/juju
	tar -C / -xvp -f juju-backup/root.tar
	mkdir -p /var/lib/juju/db
	mongorestore --drop --dbpath /var/lib/juju/db juju-backup/dump
	initctl start juju-db

	mongoEval() {
		mongo --ssl -u admin -p {{.AdminSecret | shquote}} localhost:37017/admin --eval "$1"
	}
	# wait for mongo to come up after starting the juju-db upstart service.
	for i in $(seq 1 60)
	do
		mongoEval ' ' && break
		sleep 2
	done
	mongoEval '
		db.machines.update({_id: 0}, {$set: {instanceid: '{{.NewInstanceId | printf "%q" | shquote}}' } })
		db.instanceData.update({_id: 0}, {$set: {instanceid: '{{.NewInstanceId | printf "%q"| shquote}}' } })
	'
#	initctl start jujud-machine-0
`)

func updateBootstrapMachineScript(instanceId instance.Id, adminSecret string) string {
	return execTemplate(updateBootstrapMachineTemplate, struct {
		NewInstanceId instance.Id
		AdminSecret string
	}{instanceId, adminSecret})
}

func (c *restoreCommand) Run(ctx *cmd.Context) error {
	if err := c.Log.Start(ctx); err != nil {
		return err
	}
	store, err := configstore.Default()
	if err != nil {
		return err
	}
	cfg, _, err := environs.ConfigForName(c.EnvName, store)
	if err != nil {
		return err
	}
	env, err := rebootstrap(cfg, c.Constraints)
	if err != nil {
		return fmt.Errorf("cannot re-bootstrap environment: %v", err)
	}
	logger.Infof("connecting to newly bootstrapped instance")
	conn, err := juju.NewAPIConn(env, api.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot connect to bootstrap instance: %v", err)
	}
	logger.Infof("restoring bootstrap machine")
	newInstId, machine0Addr, err := restoreBootstrapMachine(conn, c.backupFile)
	if err != nil {
		return fmt.Errorf("cannot restore bootstrap machine: %v", err)
	}
	logger.Infof("restored bootstrap machine")
	// Update the environ state to point to the new instance.
	if err := bootstrap.SaveState(env.Storage(), &bootstrap.BootstrapState{
		StateInstances: []instance.Id{newInstId},
	}); err != nil {
		return fmt.Errorf("cannot update environ bootstrap state storage: %v", err)
	}
	// Construct our own state info rather than using juju.NewConn so
	// that we can avoid storage eventual-consistency issues
	// (and it's faster too).
	caCert, ok := cfg.CACert()
	if !ok {
		return fmt.Errorf("configuration has no CA certificate")
	}
	logger.Infof("opening state")
	st, err := state.Open(&state.Info{
		Addrs:  []string{fmt.Sprintf("%s:%d", machine0Addr, cfg.StatePort())},
		CACert: caCert,
	}, state.DefaultDialOpts())
	if err != nil {
		return fmt.Errorf("cannot open state: %v", err)
	}
	logger.Infof("updating all machines")
	if err := updateAllMachines(st, machine0Addr); err != nil {
		return fmt.Errorf("cannot update machines: %v", err)
	}
	return nil
}

func rebootstrap(cfg *config.Config, cons constraints.Value) (environs.Environ, error) {
	logger.Infof("re-bootstrapping environment")
	// Turn on safe mode so that the newly bootstrapped instance
	// will not destroy all the instances it does not know about.
	cfg, err := cfg.Apply(map[string]interface{}{
		"provisioner-safe-mode": true,
	})
	if err != nil {
		return nil, fmt.Errorf("cannot enable provisioner-safe-mode: %v", err)
	}
	env, err := environs.New(cfg)
	if err != nil {
		return nil, err
	}
	state, err := bootstrap.LoadState(env.Storage())
	if err != nil {
		return nil, fmt.Errorf("cannot retrieve environment storage; perhaps the environment was not bootstrapped: %v", err)
	}
	if len(state.StateInstances) == 0 {
		return nil, fmt.Errorf("no instances found on bootstrap state; perhaps the environment was not bootstrapped", err)
	}
	if len(state.StateInstances) > 1 {
		return nil, fmt.Errorf("restore does not support HA juju configurations yet")
	}
	inst, err := env.Instances(state.StateInstances)
	if err == nil {
		return nil, fmt.Errorf("old bootstrap instance %q still seems to exist; will not replace", inst)
	}
	if err != environs.ErrNoInstances {
		return nil, fmt.Errorf("cannot detect whether old instance is still running: %v", err)
	}
	// Remove the storage so that we can bootstrap without the provider complaining.
	if err := env.Storage().Remove(bootstrap.StateFile); err != nil {
		return nil, fmt.Errorf("cannot remove %q from storage: %v", bootstrap.StateFile, err)
	}

	// TODO If we fail beyond here, then we won't have a state file and
	// we won't be able to re-run this script because it fails without it.
	// We could either try to recreate the file if we fail (which is itself
	// error-prone) or we could provide a --no-check flag to make
	// it go ahead anyway without the check.

	if err := bootstrap.Bootstrap(env, cons); err != nil {
		return nil, fmt.Errorf("cannot bootstrap new instance: %v", err)
	}
	return env, nil
}

func restoreBootstrapMachine(conn *juju.APIConn, backupFile string) (newInstId instance.Id, addr string, err error) {
	addr, err = conn.State.Client().PublicAddress("0")
	if err != nil {
		return "", "", fmt.Errorf("cannot get public address of bootstrap machine: %v", err)
	}
	status, err := conn.State.Client().Status()
	if err != nil {
		return "", "", fmt.Errorf("cannot get environment status: %v", err)
	}
	info, ok := status.Machines["0"]
	if !ok {
		return "", "", fmt.Errorf("cannot find bootstrap machine in status")
	}
	newInstId = instance.Id(info.InstanceId)

	if err := scp(backupFile, addr, "~/juju-backup.tgz"); err != nil {
		return "", "", fmt.Errorf("cannot copy backup file to bootstrap instance: %v", err)
	}

	adminSecret := conn.Environ.Config().AdminSecret()
	if err := ssh(addr, updateBootstrapMachineScript(newInstId, adminSecret)); err != nil {
		return "", "", fmt.Errorf("update script failed: %v", err)
	}
	return newInstId, addr, nil
}

var agentAddressTemplate = mustParseTemplate(`
set -exu
cd /var/lib/juju/agents
for agent in *
do
	initctl stop jujud-$agent
	sed -i.old -r "/^(stateaddresses|apiaddresses):/{
		n
		s/- .*(:[0-9]+)/- {{.Address}}\1/
	}" $agent/agent.conf
	if [[ $agent = unit-* ]]
	then
 		sed -i -r 's/change-version: [0-9]+$/change-version: 0/' $agent/state/relations/*/* || true
	fi
	initctl start jujud-$agent
done
sed -i -r 's/^(:syslogtag, startswith, "juju-" @)(.*)(:[0-9]+.*)$/\1{{.Address}}\3/' /etc/rsyslog.d/*-juju*.conf
`)

// setAgentAddressScript generates an ssh script argument to update state addresses
func setAgentAddressScript(stateAddr string) string {
	return execTemplate(agentAddressTemplate, struct {
		Address string
	}{stateAddr})
}

// updateAllMachines finds all machines and resets the stored state address
// in each of them. The address does not include the port.
func updateAllMachines(st *state.State, stateAddr string) error {
	machines, err := st.AllMachines()
	if err != nil {
		return err
	}
	pendingMachineCount := 0
	done := make(chan error)
	for _, machine := range machines {
		// A newly resumed state server requires no updating, and more
		// than one state server is not yet support by this plugin.
		if machine.IsManager() || machine.Life() == state.Dead {
			continue
		}
		pendingMachineCount++
		machine := machine
		go func() {
			err := runMachineUpdate(machine, setAgentAddressScript(stateAddr))
			if err != nil {
				logger.Errorf("failed to update machine %s: %v", machine, err)
			} else {
				logger.Infof("updated machine %s", machine)
			}
			done <- err
		}()
	}
	err = nil
	for ; pendingMachineCount > 0; pendingMachineCount-- {
		if updateErr := <-done; updateErr != nil && err == nil {
			err = fmt.Errorf("machine update failed")
		}
	}
	return err
}

// runMachineUpdate connects via ssh to the machine and runs the update script
func runMachineUpdate(m *state.Machine, sshArg string) error {
	logger.Infof("updating machine: %v\n", m)
	addr := instance.SelectPublicAddress(m.Addresses())
	if addr == "" {
		return fmt.Errorf("no appropriate public address found")
	}
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		sshArg,
	}
	c := exec.Command("ssh", args...)
	if data, err := c.CombinedOutput(); err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	return nil
}

func ssh(addr string, script string) error {
	args := []string{
		"-l", "ubuntu",
		"-T",
		"-o", "StrictHostKeyChecking no",
		"-o", "PasswordAuthentication no",
		addr,
		"sudo -n bash -c " + utils.ShQuote(script),
	}
	cmd := exec.Command("ssh", args...)
	logger.Debugf("ssh command: %s %q", cmd.Path, cmd.Args)
	data, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("ssh command failed: %v (%q)", err, data)
	}
	logger.Infof("ssh command succeeded: %q", data)
	return nil
}

func scp(file, host, destFile string) error {
	cmd := exec.Command("scp", "-B", "-q", file, "ubuntu@"+host+":"+destFile)
	logger.Infof("copying backup file to bootstrap host")
	logger.Debugf("scp command: %s %q", cmd.Path, cmd.Args)
	out, err := cmd.CombinedOutput()
	if err == nil {
		return nil
	}
	if _, ok := err.(*exec.ExitError); ok {
		return fmt.Errorf("scp failed: %s", out)
	}
	return err
}

func mustParseTemplate(templ string) *template.Template {
	t := template.New("").Funcs(template.FuncMap{
		"shquote": utils.ShQuote,
	})
	return template.Must(t.Parse(templ))
}

func execTemplate(tmpl *template.Template, data interface{}) string {
	var buf bytes.Buffer
	err := updateBootstrapMachineTemplate.Execute(&buf, data)
	if err != nil {
		panic(fmt.Errorf("template error: %v", err))
	}
	return buf.String()
}
<|endoftext|>


<commit_before>package acceptance_test

import (
	"cf-pusher/cf_cli_adapter"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"

	"regexp"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("external connectivity", func() {
	var (
		appA                    string
		orgName                 string
		spaceName               string
		appRoute                string
		destinationGuid         string
		stagingEgressPolicyGuid string
		cli                     *cf_cli_adapter.Adapter
		testDestination         = `{
			"destinations": [
				{
					"name": %q,
					"description": "Testing description",
					"rules": [
						{
							"protocol": "tcp",
							"ports": [ { "start": 1, "end": 65535 } ],
							"ips": [ { "start": "0.0.0.0", "end": "255.255.255.255" } ]
						},
						{
							"protocol": "icmp",
							"ips": [ { "start": "1.1.1.1", "end": "1.1.1.1" } ]
						},
						{
							"protocol": "icmp",
							"ips": [ { "start": "8.8.8.8", "end": "8.8.8.8" } ]
						}
					]
				}
			]
		}`
		testStagingEgressPolicies = `{
			"egress_policies": [ {
					"source": { "id": %q, "type": %q },
					"destination": { "id": %q },
					"app_lifecycle": "staging"
				} ]
		}`
		testRunningEgressPolicies = `{
			"egress_policies": [ {
					"source": { "id": %q, "type": %q },
					"destination": { "id": %q },
					"app_lifecycle": "running"
				} ]
		}`
		testAllEgressPolicies = `{
			"egress_policies": [ {
					"source": { "id": %q, "type": %q },
					"destination": { "id": %q },
					"app_lifecycle": "all"
				} ]
		}`
	)

	BeforeEach(func() {
		if testConfig.Internetless || testConfig.SkipExperimentalDynamicEgressTest {
			Skip("skipping egress policy tests")
		}

		cli = &cf_cli_adapter.Adapter{CfCliPath: "cf"}
		appA = fmt.Sprintf("appA-%d", rand.Int31())

		orgName = testConfig.Prefix + "egress-policy-org"
		spaceName = testConfig.Prefix + "space"
		setupOrgAndSpace(orgName, spaceName)

		By("unbinding all running ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("unbind-running-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("unbinding all staging ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("unbind-staging-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("creating a destination")
		destinationGuid = createDestination(cli, fmt.Sprintf(testDestination, fmt.Sprintf("egress-policies-%d", rand.Int31())))
	})

	AfterEach(func() {
		By("deleting destination")
		deleteDestination(cli, destinationGuid)

		By("adding back all the original staging ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("bind-staging-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("adding back all the original running ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("bind-running-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("deleting the test org")
		Expect(cf.Cf("delete-org", orgName, "-f").Wait(Timeout_Push)).To(gexec.Exit(0))
	})

	checkRequest := func(route string, expectedStatusCode int, expectedResponseRegex string) error {
		regex := regexp.MustCompile(expectedResponseRegex)
		resp, err := http.Get(route)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		respBytes, err := ioutil.ReadAll(resp.Body)
		Expect(err).NotTo(HaveOccurred())
		respBody := string(respBytes)

		if resp.StatusCode != expectedStatusCode {
			return fmt.Errorf("test http get to %s: expected response code %d but got %d.  response body:\n%s", route, expectedStatusCode, resp.StatusCode, respBody)
		}
		if !regex.MatchString(respBody) {
			return fmt.Errorf("test http get to %s: expected response to contain %q but instead saw:\n%s", route, expectedResponseRegex, respBody)
		}
		return nil
	}

	canProxy := func() error {
		return checkRequest(appRoute+"proxy/example.com", 200, `Example Domain`)
	}
	cannotProxy := func() error {
		return checkRequest(appRoute+"proxy/example.com", 500, "connection refused|i/o timeout")
	}

	canPing := func(ipAddress string) error {
		return checkRequest(appRoute+"ping/" + ipAddress, 200, `Ping succeeded to destination: ` + ipAddress)
	}

	Context("when an all egress policy is created", func() {
		BeforeEach(func() {
			By("creating staging egress policy")
			spaceGuid, err := cli.SpaceGuid(spaceName)
			Expect(err).NotTo(HaveOccurred())
			stagingEgressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testAllEgressPolicies, spaceGuid, "space", destinationGuid))
		})

		AfterEach(func() {
			By("deleting staging egress policy")
			deleteEgressPolicy(cli, stagingEgressPolicyGuid)
		})

		It("the app can reach the internet when egress policy is present", func(done Done) {
			By("pushing the test app")
			pushProxy(appA)
			appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

			By("checking that the app can use dns and http to reach the internet")
			Eventually(canProxy, "10s", "1s").Should(Succeed())
			Consistently(canProxy, "2s", "0.5s").Should(Succeed())

			Consistently(func() error { return canPing("8.8.8.8") }, "2s", "0.5s").Should(Succeed())
			Consistently(func() error { return canPing("1.1.1.1") }, "2s", "0.5s").Should(Succeed())

			close(done)
		}, 180 /* <-- overall spec timeout in seconds */)
	})

	Context("when a staging egress policy is created", func() {
		BeforeEach(func() {
			By("creating staging egress policy")
			spaceGuid, err := cli.SpaceGuid(spaceName)
			Expect(err).NotTo(HaveOccurred())
			stagingEgressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testStagingEgressPolicies, spaceGuid, "space", destinationGuid))
		})

		AfterEach(func() {
			By("deleting staging egress policy")
			deleteEgressPolicy(cli, stagingEgressPolicyGuid)
		})

		PContext("when the egress policy is for the app", func() {
			var (
				egressPolicyGuid string
			)

			AfterEach(func() {
				By("deleting egress policy")
				deleteEgressPolicy(cli, egressPolicyGuid)

				By("checking that the app cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())
			})

			It("the app can reach the internet when egress policy is present", func(done Done) {
				By("pushing the test app")
				pushProxy(appA)
				appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

				By("checking that the app cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())

				By("creating running egress policy")
				appAGuid, err := cli.AppGuid(appA)
				Expect(err).NotTo(HaveOccurred())
				egressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testRunningEgressPolicies, appAGuid, "app", destinationGuid))

				By("checking that the app can use dns and http to reach the internet")
				Eventually(canProxy, "10s", "1s").Should(Succeed())
				Consistently(canProxy, "2s", "0.5s").Should(Succeed())

				close(done)
			}, 180 /* <-- overall spec timeout in seconds */)
		})

		PContext("when the egress policy is for the space", func() {
			var (
				egressPolicyGuid string
			)

			AfterEach(func() {
				By("deleting egress policy")
				deleteEgressPolicy(cli, egressPolicyGuid)

				By("checking that the app cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())
			})

			It("the app in the space can reach the internet when egress policy is present", func(done Done) {
				By("pushing the test app")
				pushProxy(appA)
				appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

				By("checking that the space cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())

				By("creating running egress policy")
				spaceGuid, err := cli.SpaceGuid(spaceName)
				Expect(err).NotTo(HaveOccurred())
				egressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testRunningEgressPolicies, spaceGuid, "space", destinationGuid))

				By("checking that the app can use dns and http to reach the internet")
				Eventually(canProxy, "10s", "1s").Should(Succeed())
				Consistently(canProxy, "2s", "0.5s").Should(Succeed())

				close(done)
			}, 180 /* <-- overall spec timeout in seconds */)
		})

		Context("when a policy is already applied to the space", func() {
			var (
				egressPolicyGuid string
			)

			BeforeEach(func() {
				By("creating an egress policy")
				spaceGuid, err := cli.SpaceGuid(spaceName)
				Expect(err).NotTo(HaveOccurred())
				egressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testRunningEgressPolicies, spaceGuid, "space", destinationGuid))
			})

			AfterEach(func() {
				By("deleting egress policy")
				deleteEgressPolicy(cli, egressPolicyGuid)
			})

			It("the app in the space can reach the internet immediately after a push", func(done Done) {
				By("pushing the test app")
				pushProxy(appA)
				appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

				Expect(canProxy()).To(Succeed())

				close(done)
			}, 180 /* <-- overall spec timeout in seconds */)
		})
	})
})

func deleteEgressPolicy(cli *cf_cli_adapter.Adapter, guid string) {
	var egressPolicyDeleteStruct struct {
		Error string `json:"error"`
	}

	response, err := cli.Curl("DELETE", fmt.Sprintf("/networking/v1/external/egress_policies/%s", guid), "")
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &egressPolicyDeleteStruct)
	Expect(err).NotTo(HaveOccurred())
	Expect(egressPolicyDeleteStruct.Error).To(BeEmpty())
}

func createEgressPolicy(cli *cf_cli_adapter.Adapter, payload string) string {
	payloadFile, err := ioutil.TempFile("", "")
	Expect(err).NotTo(HaveOccurred())

	var egressPolicyStruct struct {
		EgressPolicies []struct {
			ID string `json:"id"`
		} `json:"egress_policies"`
		Error string `json:"error"`
	}

	_, err = payloadFile.Write([]byte(payload))
	Expect(err).NotTo(HaveOccurred())

	err = payloadFile.Close()
	Expect(err).NotTo(HaveOccurred())

	response, err := cli.Curl("POST", "/networking/v1/external/egress_policies", payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &egressPolicyStruct)
	Expect(err).NotTo(HaveOccurred())
	Expect(egressPolicyStruct.Error).To(BeEmpty())

	err = os.Remove(payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())
	return egressPolicyStruct.EgressPolicies[0].ID
}

func createDestination(cli *cf_cli_adapter.Adapter, payload string) string {
	payloadFile, err := ioutil.TempFile("", "")
	Expect(err).NotTo(HaveOccurred())

	var destStruct struct {
		Destinations []struct {
			ID string `json:"id"`
		} `json:"destinations"`
		Error string `json:"error"`
	}

	_, err = payloadFile.Write([]byte(payload))
	Expect(err).NotTo(HaveOccurred())

	err = payloadFile.Close()
	Expect(err).NotTo(HaveOccurred())

	response, err := cli.Curl("POST", "/networking/v1/external/destinations", payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &destStruct)
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("cannot unmarshal json: %s", response))
	Expect(destStruct.Error).To(BeEmpty(), destStruct.Error)

	err = os.Remove(payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())

	return destStruct.Destinations[0].ID
}

func deleteDestination(cli *cf_cli_adapter.Adapter, guid string) {
	var destDeleteStruct struct {
		Error string `json:"error"`
	}

	response, err := cli.Curl("DELETE", fmt.Sprintf("/networking/v1/external/destinations/%s", guid), "")
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &destDeleteStruct)
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("cannot unmarshal json: %s", response))
	Expect(destDeleteStruct.Error).To(BeEmpty())
}
<commit_msg>Revert "Pend tests while we wait for CAPI to merge PRs"<commit_after>package acceptance_test

import (
	"cf-pusher/cf_cli_adapter"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"os"

	"regexp"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gexec"
)

var _ = Describe("external connectivity", func() {
	var (
		appA                    string
		orgName                 string
		spaceName               string
		appRoute                string
		destinationGuid         string
		stagingEgressPolicyGuid string
		cli                     *cf_cli_adapter.Adapter
		testDestination         = `{
			"destinations": [
				{
					"name": %q,
					"description": "Testing description",
					"rules": [
						{
							"protocol": "tcp",
							"ports": [ { "start": 1, "end": 65535 } ],
							"ips": [ { "start": "0.0.0.0", "end": "255.255.255.255" } ]
						},
						{
							"protocol": "icmp",
							"ips": [ { "start": "1.1.1.1", "end": "1.1.1.1" } ]
						},
						{
							"protocol": "icmp",
							"ips": [ { "start": "8.8.8.8", "end": "8.8.8.8" } ]
						}
					]
				}
			]
		}`
		testStagingEgressPolicies = `{
			"egress_policies": [ {
					"source": { "id": %q, "type": %q },
					"destination": { "id": %q },
					"app_lifecycle": "staging"
				} ]
		}`
		testRunningEgressPolicies = `{
			"egress_policies": [ {
					"source": { "id": %q, "type": %q },
					"destination": { "id": %q },
					"app_lifecycle": "running"
				} ]
		}`
		testAllEgressPolicies = `{
			"egress_policies": [ {
					"source": { "id": %q, "type": %q },
					"destination": { "id": %q },
					"app_lifecycle": "all"
				} ]
		}`
	)

	BeforeEach(func() {
		if testConfig.Internetless || testConfig.SkipExperimentalDynamicEgressTest {
			Skip("skipping egress policy tests")
		}

		cli = &cf_cli_adapter.Adapter{CfCliPath: "cf"}
		appA = fmt.Sprintf("appA-%d", rand.Int31())

		orgName = testConfig.Prefix + "egress-policy-org"
		spaceName = testConfig.Prefix + "space"
		setupOrgAndSpace(orgName, spaceName)

		By("unbinding all running ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("unbind-running-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("unbinding all staging ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("unbind-staging-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("creating a destination")
		destinationGuid = createDestination(cli, fmt.Sprintf(testDestination, fmt.Sprintf("egress-policies-%d", rand.Int31())))
	})

	AfterEach(func() {
		By("deleting destination")
		deleteDestination(cli, destinationGuid)

		By("adding back all the original staging ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("bind-staging-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("adding back all the original running ASGs")
		for _, sg := range testConfig.DefaultSecurityGroups {
			Expect(cf.Cf("bind-running-security-group", sg).Wait(Timeout_Short)).To(gexec.Exit(0))
		}

		By("deleting the test org")
		Expect(cf.Cf("delete-org", orgName, "-f").Wait(Timeout_Push)).To(gexec.Exit(0))
	})

	checkRequest := func(route string, expectedStatusCode int, expectedResponseRegex string) error {
		regex := regexp.MustCompile(expectedResponseRegex)
		resp, err := http.Get(route)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		respBytes, err := ioutil.ReadAll(resp.Body)
		Expect(err).NotTo(HaveOccurred())
		respBody := string(respBytes)

		if resp.StatusCode != expectedStatusCode {
			return fmt.Errorf("test http get to %s: expected response code %d but got %d.  response body:\n%s", route, expectedStatusCode, resp.StatusCode, respBody)
		}
		if !regex.MatchString(respBody) {
			return fmt.Errorf("test http get to %s: expected response to contain %q but instead saw:\n%s", route, expectedResponseRegex, respBody)
		}
		return nil
	}

	canProxy := func() error {
		return checkRequest(appRoute+"proxy/example.com", 200, `Example Domain`)
	}
	cannotProxy := func() error {
		return checkRequest(appRoute+"proxy/example.com", 500, "connection refused|i/o timeout")
	}

	canPing := func(ipAddress string) error {
		return checkRequest(appRoute+"ping/" + ipAddress, 200, `Ping succeeded to destination: ` + ipAddress)
	}

	Context("when an all egress policy is created", func() {
		BeforeEach(func() {
			By("creating staging egress policy")
			spaceGuid, err := cli.SpaceGuid(spaceName)
			Expect(err).NotTo(HaveOccurred())
			stagingEgressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testAllEgressPolicies, spaceGuid, "space", destinationGuid))
		})

		AfterEach(func() {
			By("deleting staging egress policy")
			deleteEgressPolicy(cli, stagingEgressPolicyGuid)
		})

		It("the app can reach the internet when egress policy is present", func(done Done) {
			By("pushing the test app")
			pushProxy(appA)
			appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

			By("checking that the app can use dns and http to reach the internet")
			Eventually(canProxy, "10s", "1s").Should(Succeed())
			Consistently(canProxy, "2s", "0.5s").Should(Succeed())

			Consistently(func() error { return canPing("8.8.8.8") }, "2s", "0.5s").Should(Succeed())
			Consistently(func() error { return canPing("1.1.1.1") }, "2s", "0.5s").Should(Succeed())

			close(done)
		}, 180 /* <-- overall spec timeout in seconds */)
	})

	Context("when a staging egress policy is created", func() {
		BeforeEach(func() {
			By("creating staging egress policy")
			spaceGuid, err := cli.SpaceGuid(spaceName)
			Expect(err).NotTo(HaveOccurred())
			stagingEgressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testStagingEgressPolicies, spaceGuid, "space", destinationGuid))
		})

		AfterEach(func() {
			By("deleting staging egress policy")
			deleteEgressPolicy(cli, stagingEgressPolicyGuid)
		})

		Context("when the egress policy is for the app", func() {
			var (
				egressPolicyGuid string
			)

			AfterEach(func() {
				By("deleting egress policy")
				deleteEgressPolicy(cli, egressPolicyGuid)

				By("checking that the app cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())
			})

			It("the app can reach the internet when egress policy is present", func(done Done) {
				By("pushing the test app")
				pushProxy(appA)
				appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

				By("checking that the app cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())

				By("creating running egress policy")
				appAGuid, err := cli.AppGuid(appA)
				Expect(err).NotTo(HaveOccurred())
				egressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testRunningEgressPolicies, appAGuid, "app", destinationGuid))

				By("checking that the app can use dns and http to reach the internet")
				Eventually(canProxy, "10s", "1s").Should(Succeed())
				Consistently(canProxy, "2s", "0.5s").Should(Succeed())

				close(done)
			}, 180 /* <-- overall spec timeout in seconds */)
		})

		Context("when the egress policy is for the space", func() {
			var (
				egressPolicyGuid string
			)

			AfterEach(func() {
				By("deleting egress policy")
				deleteEgressPolicy(cli, egressPolicyGuid)

				By("checking that the app cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())
			})

			It("the app in the space can reach the internet when egress policy is present", func(done Done) {
				By("pushing the test app")
				pushProxy(appA)
				appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

				By("checking that the space cannot reach the internet using http and dns")
				Eventually(cannotProxy, "10s", "1s").Should(Succeed())
				Consistently(cannotProxy, "2s", "0.5s").Should(Succeed())

				By("creating running egress policy")
				spaceGuid, err := cli.SpaceGuid(spaceName)
				Expect(err).NotTo(HaveOccurred())
				egressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testRunningEgressPolicies, spaceGuid, "space", destinationGuid))

				By("checking that the app can use dns and http to reach the internet")
				Eventually(canProxy, "10s", "1s").Should(Succeed())
				Consistently(canProxy, "2s", "0.5s").Should(Succeed())

				close(done)
			}, 180 /* <-- overall spec timeout in seconds */)
		})

		Context("when a policy is already applied to the space", func() {
			var (
				egressPolicyGuid string
			)

			BeforeEach(func() {
				By("creating an egress policy")
				spaceGuid, err := cli.SpaceGuid(spaceName)
				Expect(err).NotTo(HaveOccurred())
				egressPolicyGuid = createEgressPolicy(cli, fmt.Sprintf(testRunningEgressPolicies, spaceGuid, "space", destinationGuid))
			})

			AfterEach(func() {
				By("deleting egress policy")
				deleteEgressPolicy(cli, egressPolicyGuid)
			})

			It("the app in the space can reach the internet immediately after a push", func(done Done) {
				By("pushing the test app")
				pushProxy(appA)
				appRoute = fmt.Sprintf("http://%s.%s/", appA, config.AppsDomain)

				Expect(canProxy()).To(Succeed())

				close(done)
			}, 180 /* <-- overall spec timeout in seconds */)
		})
	})
})

func deleteEgressPolicy(cli *cf_cli_adapter.Adapter, guid string) {
	var egressPolicyDeleteStruct struct {
		Error string `json:"error"`
	}

	response, err := cli.Curl("DELETE", fmt.Sprintf("/networking/v1/external/egress_policies/%s", guid), "")
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &egressPolicyDeleteStruct)
	Expect(err).NotTo(HaveOccurred())
	Expect(egressPolicyDeleteStruct.Error).To(BeEmpty())
}

func createEgressPolicy(cli *cf_cli_adapter.Adapter, payload string) string {
	payloadFile, err := ioutil.TempFile("", "")
	Expect(err).NotTo(HaveOccurred())

	var egressPolicyStruct struct {
		EgressPolicies []struct {
			ID string `json:"id"`
		} `json:"egress_policies"`
		Error string `json:"error"`
	}

	_, err = payloadFile.Write([]byte(payload))
	Expect(err).NotTo(HaveOccurred())

	err = payloadFile.Close()
	Expect(err).NotTo(HaveOccurred())

	response, err := cli.Curl("POST", "/networking/v1/external/egress_policies", payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &egressPolicyStruct)
	Expect(err).NotTo(HaveOccurred())
	Expect(egressPolicyStruct.Error).To(BeEmpty())

	err = os.Remove(payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())
	return egressPolicyStruct.EgressPolicies[0].ID
}

func createDestination(cli *cf_cli_adapter.Adapter, payload string) string {
	payloadFile, err := ioutil.TempFile("", "")
	Expect(err).NotTo(HaveOccurred())

	var destStruct struct {
		Destinations []struct {
			ID string `json:"id"`
		} `json:"destinations"`
		Error string `json:"error"`
	}

	_, err = payloadFile.Write([]byte(payload))
	Expect(err).NotTo(HaveOccurred())

	err = payloadFile.Close()
	Expect(err).NotTo(HaveOccurred())

	response, err := cli.Curl("POST", "/networking/v1/external/destinations", payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &destStruct)
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("cannot unmarshal json: %s", response))
	Expect(destStruct.Error).To(BeEmpty(), destStruct.Error)

	err = os.Remove(payloadFile.Name())
	Expect(err).NotTo(HaveOccurred())

	return destStruct.Destinations[0].ID
}

func deleteDestination(cli *cf_cli_adapter.Adapter, guid string) {
	var destDeleteStruct struct {
		Error string `json:"error"`
	}

	response, err := cli.Curl("DELETE", fmt.Sprintf("/networking/v1/external/destinations/%s", guid), "")
	Expect(err).NotTo(HaveOccurred())
	err = json.Unmarshal(response, &destDeleteStruct)
	Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("cannot unmarshal json: %s", response))
	Expect(destDeleteStruct.Error).To(BeEmpty())
}
<|endoftext|>