<commit_before>package main

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

	"golang.org/x/net/context"
)

const maxAgeSecondsRedirCookie = 120
const redirCookieName = "oauth2_redir"

const oauth2LoginBeginPath = "/auth/oauth2/login"

func (state *RuntimeState) oauth2DoRedirectoToProviderHandler(
	w http.ResponseWriter, r *http.Request) {
	if state.Config.Oauth2.Config == nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError,
			"error internal")
		logger.Println("asking for oauth2, but it is not defined")
		return
	}
	if !state.Config.Oauth2.Enabled {
		state.writeFailureResponse(w, r, http.StatusBadRequest,
			"Oauth2 is not enabled in for this system")
		logger.Println("asking for oauth2, but it is not enabled")
		return
	}
	cookieVal, err := genRandomString()
	if err != nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError,
			"error internal")
		logger.Println(err)
		return
	}
	// we have to create new context and set redirector...
	expiration := time.Now().Add(time.Duration(maxAgeSecondsRedirCookie) *
		time.Second)
	stateString, err := genRandomString()
	if err != nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError,
			"error internal")
		logger.Println(err)
		return
	}
	cookie := http.Cookie{
		Name:     redirCookieName,
		Value:    cookieVal,
		Expires:  expiration,
		Path:     "/",
		HttpOnly: true,
	}
	http.SetCookie(w, &cookie)
	pending := pendingAuth2Request{
		ctx:              context.Background(),
		ExpiresAt:        expiration,
		loginDestination: r.FormValue("login_destination"),
		state:            stateString,
	}
	state.Mutex.Lock()
	state.pendingOauth2[cookieVal] = pending
	state.Mutex.Unlock()
	http.Redirect(w, r, state.Config.Oauth2.Config.AuthCodeURL(stateString),
		http.StatusFound)
}

func httpGet(client *http.Client, url string) ([]byte, error) {
	r, err := client.Get(url)
	if err != nil {
		return nil, err
	}

	defer r.Body.Close()

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	if r.StatusCode >= 300 {
		return nil, fmt.Errorf(string(body))
	}

	logger.Debugf(8, "HTTP GET %s: %s %s", url, r.Status, string(body))

	return body, nil
}

func (state *RuntimeState) oauth2RedirectPathHandler(w http.ResponseWriter, r *http.Request) {

	if state.Config.Oauth2.Config == nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError, "error internal")
		logger.Println("asking for oauth2, but it is not defined")
		return
	}
	if !state.Config.Oauth2.Enabled {
		state.writeFailureResponse(w, r, http.StatusBadRequest, "Oauth2 is not enabled in for this system")
		logger.Println("asking for oauth2, but it is not enabled")
		return
	}

	redirCookie, err := r.Cookie(redirCookieName)
	if err != nil {
		if err == http.ErrNoCookie {
			state.writeFailureResponse(w, r, http.StatusBadRequest, "Missing setup cookie!")
			logger.Println(err)
			return
		}
		// TODO: this is probably a user error? send back to oath2 login path?
		state.writeFailureResponse(w, r, http.StatusInternalServerError, "error internal")
		logger.Println(err)
		return
	}
	index := redirCookie.Value
	state.Mutex.Lock()
	pending, ok := state.pendingOauth2[index]
	state.Mutex.Unlock()
	if !ok {
		// clear cookie here!!!!
		state.writeFailureResponse(w, r, http.StatusBadRequest, "Invalid setup cookie!")
		logger.Println(err)
		return
	}

	if r.URL.Query().Get("state") != pending.state {
		logger.Printf("state does not match")
		http.Error(w, "state did not match", http.StatusBadRequest)
		return
	}
	//if Debug {
	//logger.Printf("req : %+v", r)
	//}
	oauth2Token, err := state.Config.Oauth2.Config.Exchange(pending.ctx, r.URL.Query().Get("code"))
	if err != nil {
		logger.Printf("failed to get token: ctx: %+v", pending.ctx)
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}
	client := state.Config.Oauth2.Config.Client(pending.ctx, oauth2Token)
	//client.Get("...")
	body, err := httpGet(client, state.Config.Oauth2.UserinfoUrl)
	if err != nil {
		logger.Printf("fail to fetch %s (%s) ", state.Config.Oauth2.UserinfoUrl, err.Error())
		http.Error(w, "Failed to get userinfo from url: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var data struct {
		Name        string              `json:"name"`
		DisplayName string              `json:"display_name"`
		Login       string              `json:"login"`
		Username    string              `json:"username"`
		Email       string              `json:"email"`
		Attributes  map[string][]string `json:"attributes"`
	}

	logger.Debugf(3, "Userinfo body:'%s'", string(body))
	err = json.Unmarshal(body, &data)
	if err != nil {
		logger.Printf("failed to unmarshall userinfo to fetch %s ", body)
		http.Error(w, "Failed to get unmarshall userinfo: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// The Name field could also be useful
	logger.Debugf(2, "%+v", data)

	// Check if name is there..

	// TODO: we need a more robust way to get the username and to add some filters. This
	// mechanism is ok for 0.2 but not for 0.3.
	username := data.Login
	if username == "" {
		components := strings.Split(data.Email, "@")
		if len(components[0]) < 1 {
			http.Error(w, "Email from userinfo is invalid: ", http.StatusInternalServerError)
			return
		}
		username = strings.ToLower(components[0])
	}

	//Make new auth cookie
	_, err = state.setNewAuthCookie(w, username, AuthTypeFederated)
	if err != nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError, "error internal")
		logger.Println(err)
		return
	}

	// delete peding cookie
	state.Mutex.Lock()
	delete(state.pendingOauth2, index)
	state.Mutex.Unlock()

	eventNotifier.PublishWebLoginEvent(username)
	loginDestination := pending.loginDestination
	if loginDestination == "" {
		// Nowhere else to go: go to profile page.
		loginDestination = profilePath
	}
	http.Redirect(w, r, loginDestination, 302)
}
<commit_msg>Add filtering of login destination.<commit_after>package main

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

	"golang.org/x/net/context"
)

const maxAgeSecondsRedirCookie = 120
const redirCookieName = "oauth2_redir"

const oauth2LoginBeginPath = "/auth/oauth2/login"

func (state *RuntimeState) oauth2DoRedirectoToProviderHandler(
	w http.ResponseWriter, r *http.Request) {
	if state.Config.Oauth2.Config == nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError,
			"error internal")
		logger.Println("asking for oauth2, but it is not defined")
		return
	}
	if !state.Config.Oauth2.Enabled {
		state.writeFailureResponse(w, r, http.StatusBadRequest,
			"Oauth2 is not enabled in for this system")
		logger.Println("asking for oauth2, but it is not enabled")
		return
	}
	cookieVal, err := genRandomString()
	if err != nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError,
			"error internal")
		logger.Println(err)
		return
	}
	// we have to create new context and set redirector...
	expiration := time.Now().Add(time.Duration(maxAgeSecondsRedirCookie) *
		time.Second)
	stateString, err := genRandomString()
	if err != nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError,
			"error internal")
		logger.Println(err)
		return
	}
	cookie := http.Cookie{
		Name:     redirCookieName,
		Value:    cookieVal,
		Expires:  expiration,
		Path:     "/",
		HttpOnly: true,
	}
	http.SetCookie(w, &cookie)
	pending := pendingAuth2Request{
		ctx:              context.Background(),
		ExpiresAt:        expiration,
		loginDestination: getLoginDestination(r),
		state:            stateString,
	}
	state.Mutex.Lock()
	state.pendingOauth2[cookieVal] = pending
	state.Mutex.Unlock()
	http.Redirect(w, r, state.Config.Oauth2.Config.AuthCodeURL(stateString),
		http.StatusFound)
}

func httpGet(client *http.Client, url string) ([]byte, error) {
	r, err := client.Get(url)
	if err != nil {
		return nil, err
	}

	defer r.Body.Close()

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, err
	}

	if r.StatusCode >= 300 {
		return nil, fmt.Errorf(string(body))
	}

	logger.Debugf(8, "HTTP GET %s: %s %s", url, r.Status, string(body))

	return body, nil
}

func (state *RuntimeState) oauth2RedirectPathHandler(w http.ResponseWriter, r *http.Request) {

	if state.Config.Oauth2.Config == nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError, "error internal")
		logger.Println("asking for oauth2, but it is not defined")
		return
	}
	if !state.Config.Oauth2.Enabled {
		state.writeFailureResponse(w, r, http.StatusBadRequest, "Oauth2 is not enabled in for this system")
		logger.Println("asking for oauth2, but it is not enabled")
		return
	}

	redirCookie, err := r.Cookie(redirCookieName)
	if err != nil {
		if err == http.ErrNoCookie {
			state.writeFailureResponse(w, r, http.StatusBadRequest, "Missing setup cookie!")
			logger.Println(err)
			return
		}
		// TODO: this is probably a user error? send back to oath2 login path?
		state.writeFailureResponse(w, r, http.StatusInternalServerError, "error internal")
		logger.Println(err)
		return
	}
	index := redirCookie.Value
	state.Mutex.Lock()
	pending, ok := state.pendingOauth2[index]
	state.Mutex.Unlock()
	if !ok {
		// clear cookie here!!!!
		state.writeFailureResponse(w, r, http.StatusBadRequest, "Invalid setup cookie!")
		logger.Println(err)
		return
	}

	if r.URL.Query().Get("state") != pending.state {
		logger.Printf("state does not match")
		http.Error(w, "state did not match", http.StatusBadRequest)
		return
	}
	//if Debug {
	//logger.Printf("req : %+v", r)
	//}
	oauth2Token, err := state.Config.Oauth2.Config.Exchange(pending.ctx, r.URL.Query().Get("code"))
	if err != nil {
		logger.Printf("failed to get token: ctx: %+v", pending.ctx)
		http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
		return
	}
	client := state.Config.Oauth2.Config.Client(pending.ctx, oauth2Token)
	//client.Get("...")
	body, err := httpGet(client, state.Config.Oauth2.UserinfoUrl)
	if err != nil {
		logger.Printf("fail to fetch %s (%s) ", state.Config.Oauth2.UserinfoUrl, err.Error())
		http.Error(w, "Failed to get userinfo from url: "+err.Error(), http.StatusInternalServerError)
		return
	}

	var data struct {
		Name        string              `json:"name"`
		DisplayName string              `json:"display_name"`
		Login       string              `json:"login"`
		Username    string              `json:"username"`
		Email       string              `json:"email"`
		Attributes  map[string][]string `json:"attributes"`
	}

	logger.Debugf(3, "Userinfo body:'%s'", string(body))
	err = json.Unmarshal(body, &data)
	if err != nil {
		logger.Printf("failed to unmarshall userinfo to fetch %s ", body)
		http.Error(w, "Failed to get unmarshall userinfo: "+err.Error(), http.StatusInternalServerError)
		return
	}

	// The Name field could also be useful
	logger.Debugf(2, "%+v", data)

	// Check if name is there..

	// TODO: we need a more robust way to get the username and to add some filters. This
	// mechanism is ok for 0.2 but not for 0.3.
	username := data.Login
	if username == "" {
		components := strings.Split(data.Email, "@")
		if len(components[0]) < 1 {
			http.Error(w, "Email from userinfo is invalid: ", http.StatusInternalServerError)
			return
		}
		username = strings.ToLower(components[0])
	}

	//Make new auth cookie
	_, err = state.setNewAuthCookie(w, username, AuthTypeFederated)
	if err != nil {
		state.writeFailureResponse(w, r, http.StatusInternalServerError, "error internal")
		logger.Println(err)
		return
	}

	// delete peding cookie
	state.Mutex.Lock()
	delete(state.pendingOauth2, index)
	state.Mutex.Unlock()

	eventNotifier.PublishWebLoginEvent(username)
	loginDestination := pending.loginDestination
	if loginDestination == "" {
		// Nowhere else to go: go to profile page.
		loginDestination = profilePath
	}
	http.Redirect(w, r, loginDestination, 302)
}
<|endoftext|>


<commit_before>package project

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"runtime"

	"github.com/pkg/errors"

	"github.com/spf13/cobra"

	"github.com/mutagen-io/mutagen/cmd/mutagen/forward"
	"github.com/mutagen-io/mutagen/cmd/mutagen/sync"
	projectcfg "github.com/mutagen-io/mutagen/pkg/configuration/project"
	"github.com/mutagen-io/mutagen/pkg/filesystem/locking"
	"github.com/mutagen-io/mutagen/pkg/identifier"
	"github.com/mutagen-io/mutagen/pkg/project"
)

func resumeMain(command *cobra.Command, arguments []string) error {
	// Validate arguments.
	if len(arguments) > 0 {
		return errors.New("unexpected arguments provided")
	}

	// Compute the name of the configuration file and ensure that our working
	// directory is that in which the file resides. This is required for
	// relative paths (including relative synchronization paths and relative
	// Unix Domain Socket paths) to be resolved relative to the project
	// configuration file.
	configurationFileName := project.DefaultConfigurationFileName
	if resumeConfiguration.projectFile != "" {
		var directory string
		directory, configurationFileName = filepath.Split(resumeConfiguration.projectFile)
		if directory != "" {
			if err := os.Chdir(directory); err != nil {
				return errors.Wrap(err, "unable to switch to target directory")
			}
		}
	}

	// Compute the lock path.
	lockPath := configurationFileName + project.LockFileExtension

	// Track whether or not we should remove the lock file on return.
	var removeLockFileOnReturn bool

	// Create a locker and defer its closure and potential removal. On Windows
	// systems, we have to handle this removal after the file is closed.
	locker, err := locking.NewLocker(lockPath, 0600)
	if err != nil {
		return errors.Wrap(err, "unable to create project locker")
	}
	defer func() {
		locker.Close()
		if removeLockFileOnReturn && runtime.GOOS == "windows" {
			os.Remove(lockPath)
		}
	}()

	// Acquire the project lock and defer its release and potential removal. On
	// Windows systems, we can't remove the lock file if it's locked or even
	// just opened, so we handle removal for Windows systems after we close the
	// lock file (see above). In this case, we truncate the lock file before
	// releasing it to ensure that any other process that opens or acquires the
	// lock file before we manage to remove it will simply see an empty lock
	// file, which it will ignore or attempt to remove.
	if err := locker.Lock(true); err != nil {
		return errors.Wrap(err, "unable to acquire project lock")
	}
	defer func() {
		if removeLockFileOnReturn {
			if runtime.GOOS == "windows" {
				locker.Truncate(0)
			} else {
				os.Remove(lockPath)
			}
		}
		locker.Unlock()
	}()

	// Read the project identifier from the lock file. If the lock file is
	// empty, then we can assume that we created it when we created the lock and
	// just remove it.
	buffer := &bytes.Buffer{}
	if length, err := buffer.ReadFrom(locker); err != nil {
		return errors.Wrap(err, "unable to read project lock")
	} else if length == 0 {
		removeLockFileOnReturn = true
		return errors.New("project not running")
	}
	projectIdentifier := buffer.String()

	// Ensure that the project identifier is valid.
	if !identifier.IsValid(projectIdentifier) {
		return errors.New("invalid project identifier found in project lock")
	}

	// Load the configuration file.
	configuration, err := projectcfg.LoadConfiguration(configurationFileName)
	if err != nil {
		return errors.Wrap(err, "unable to load configuration file")
	}

	// Perform pre-resumption commands.
	for _, command := range configuration.BeforeResume {
		fmt.Println(">", command)
		if err := runInShell(command); err != nil {
			return errors.Wrap(err, "pre-resume command failed")
		}
	}

	// Compute the label selector that we're going to use to resume sessions.
	labelSelector := fmt.Sprintf("%s=%s", project.LabelKey, projectIdentifier)

	// Resume forwarding sessions.
	if err := forward.ResumeWithLabelSelector(labelSelector); err != nil {
		return errors.Wrap(err, "unable to resume forwarding session(s)")
	}

	// Resume synchronization sessions.
	if err := sync.ResumeWithLabelSelector(labelSelector); err != nil {
		return errors.Wrap(err, "unable to resume synchronization session(s)")
	}

	// Perform post-resume commands.
	for _, command := range configuration.AfterResume {
		fmt.Println(">", command)
		if err := runInShell(command); err != nil {
			return errors.Wrap(err, "post-resume command failed")
		}
	}

	// Success.
	return nil
}

var resumeCommand = &cobra.Command{
	Use:          "resume",
	Short:        "Resume project sessions",
	RunE:         resumeMain,
	SilenceUsage: true,
}

var resumeConfiguration struct {
	// help indicates whether or not to show help information and exit.
	help bool
	// projectFile is the path to the project file, if non-default.
	projectFile string
}

func init() {
	// Grab a handle for the command line flags.
	flags := resumeCommand.Flags()

	// Disable alphabetical sorting of flags in help output.
	flags.SortFlags = false

	// Manually add a help flag to override the default message. Cobra will
	// still implement its logic automatically.
	flags.BoolVarP(&resumeConfiguration.help, "help", "h", false, "Show help information")

	// Wire up project file flags.
	flags.StringVarP(&resumeConfiguration.projectFile, "project-file", "f", "", "Specify project file")
}
<commit_msg>Fixed issue with comment consistency.<commit_after>package project

import (
	"bytes"
	"fmt"
	"os"
	"path/filepath"
	"runtime"

	"github.com/pkg/errors"

	"github.com/spf13/cobra"

	"github.com/mutagen-io/mutagen/cmd/mutagen/forward"
	"github.com/mutagen-io/mutagen/cmd/mutagen/sync"
	projectcfg "github.com/mutagen-io/mutagen/pkg/configuration/project"
	"github.com/mutagen-io/mutagen/pkg/filesystem/locking"
	"github.com/mutagen-io/mutagen/pkg/identifier"
	"github.com/mutagen-io/mutagen/pkg/project"
)

func resumeMain(command *cobra.Command, arguments []string) error {
	// Validate arguments.
	if len(arguments) > 0 {
		return errors.New("unexpected arguments provided")
	}

	// Compute the name of the configuration file and ensure that our working
	// directory is that in which the file resides. This is required for
	// relative paths (including relative synchronization paths and relative
	// Unix Domain Socket paths) to be resolved relative to the project
	// configuration file.
	configurationFileName := project.DefaultConfigurationFileName
	if resumeConfiguration.projectFile != "" {
		var directory string
		directory, configurationFileName = filepath.Split(resumeConfiguration.projectFile)
		if directory != "" {
			if err := os.Chdir(directory); err != nil {
				return errors.Wrap(err, "unable to switch to target directory")
			}
		}
	}

	// Compute the lock path.
	lockPath := configurationFileName + project.LockFileExtension

	// Track whether or not we should remove the lock file on return.
	var removeLockFileOnReturn bool

	// Create a locker and defer its closure and potential removal. On Windows
	// systems, we have to handle this removal after the file is closed.
	locker, err := locking.NewLocker(lockPath, 0600)
	if err != nil {
		return errors.Wrap(err, "unable to create project locker")
	}
	defer func() {
		locker.Close()
		if removeLockFileOnReturn && runtime.GOOS == "windows" {
			os.Remove(lockPath)
		}
	}()

	// Acquire the project lock and defer its release and potential removal. On
	// Windows systems, we can't remove the lock file if it's locked or even
	// just opened, so we handle removal for Windows systems after we close the
	// lock file (see above). In this case, we truncate the lock file before
	// releasing it to ensure that any other process that opens or acquires the
	// lock file before we manage to remove it will simply see an empty lock
	// file, which it will ignore or attempt to remove.
	if err := locker.Lock(true); err != nil {
		return errors.Wrap(err, "unable to acquire project lock")
	}
	defer func() {
		if removeLockFileOnReturn {
			if runtime.GOOS == "windows" {
				locker.Truncate(0)
			} else {
				os.Remove(lockPath)
			}
		}
		locker.Unlock()
	}()

	// Read the project identifier from the lock file. If the lock file is
	// empty, then we can assume that we created it when we created the lock and
	// just remove it.
	buffer := &bytes.Buffer{}
	if length, err := buffer.ReadFrom(locker); err != nil {
		return errors.Wrap(err, "unable to read project lock")
	} else if length == 0 {
		removeLockFileOnReturn = true
		return errors.New("project not running")
	}
	projectIdentifier := buffer.String()

	// Ensure that the project identifier is valid.
	if !identifier.IsValid(projectIdentifier) {
		return errors.New("invalid project identifier found in project lock")
	}

	// Load the configuration file.
	configuration, err := projectcfg.LoadConfiguration(configurationFileName)
	if err != nil {
		return errors.Wrap(err, "unable to load configuration file")
	}

	// Perform pre-resume commands.
	for _, command := range configuration.BeforeResume {
		fmt.Println(">", command)
		if err := runInShell(command); err != nil {
			return errors.Wrap(err, "pre-resume command failed")
		}
	}

	// Compute the label selector that we're going to use to resume sessions.
	labelSelector := fmt.Sprintf("%s=%s", project.LabelKey, projectIdentifier)

	// Resume forwarding sessions.
	if err := forward.ResumeWithLabelSelector(labelSelector); err != nil {
		return errors.Wrap(err, "unable to resume forwarding session(s)")
	}

	// Resume synchronization sessions.
	if err := sync.ResumeWithLabelSelector(labelSelector); err != nil {
		return errors.Wrap(err, "unable to resume synchronization session(s)")
	}

	// Perform post-resume commands.
	for _, command := range configuration.AfterResume {
		fmt.Println(">", command)
		if err := runInShell(command); err != nil {
			return errors.Wrap(err, "post-resume command failed")
		}
	}

	// Success.
	return nil
}

var resumeCommand = &cobra.Command{
	Use:          "resume",
	Short:        "Resume project sessions",
	RunE:         resumeMain,
	SilenceUsage: true,
}

var resumeConfiguration struct {
	// help indicates whether or not to show help information and exit.
	help bool
	// projectFile is the path to the project file, if non-default.
	projectFile string
}

func init() {
	// Grab a handle for the command line flags.
	flags := resumeCommand.Flags()

	// Disable alphabetical sorting of flags in help output.
	flags.SortFlags = false

	// Manually add a help flag to override the default message. Cobra will
	// still implement its logic automatically.
	flags.BoolVarP(&resumeConfiguration.help, "help", "h", false, "Show help information")

	// Wire up project file flags.
	flags.StringVarP(&resumeConfiguration.projectFile, "project-file", "f", "", "Specify project file")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"firepear.net/aclient"
	"github.com/sboyettedh/whiplash"
)

var (
	whipconf string
	hostname string
	acconf *aclient.Config
	req []byte
	// which interval set to use for tickers
	intv int
	// the interval sets
	intvs = []map[string]time.Duration{
		{"ping": 11},
		{"ping": 13},
		{"ping": 17},
		{"ping": 19},
	}
)

func init() {
	flag.StringVar(&whipconf, "whipconf", "/etc/whiplash.json", "Whiplash configuration file")
	hostname, _ = os.LookupEnv("HOSTNAME")
	req = []byte("osdupdate ")
}

func clientInit(fn string) (chan os.Signal, error) {
	// set up logfile
	f, err := os.Create("/var/log/" + fn + ".log")
	if err != nil {
		return nil, err
	}
	log.SetOutput(f)
	// write pidfile
	pidstr := strconv.Itoa(os.Getpid()) + "\n"
	err = ioutil.WriteFile("/var/run/" + fn + ".pid", []byte(pidstr), 0644)
	if err != nil {
		return nil, err
	}
	// and register SIGINT/SIGTERM handler
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)
	return sigchan, err
}

func main() {
	flag.Parse()
	sigchan, err := clientInit("whiplash-client")
	if err != nil{
		log.Fatal(err)
	}
	log.Printf("whiplash-client v%v beginning operations\n", whiplash.Version)

	wl, err := whiplash.New(whipconf, true)
	if err != nil {
		log.Printf("%v: could not read configuration file: %v\n", os.Args[0], err)
		os.Exit(1)
	}

	// need an aclient configuration to talk to the aggregator with
	acconf = &aclient.Config{
		Addr: wl.Aggregator.BindAddr + ":" + wl.Aggregator.BindPort,
		Timeout: 100,
	}

	// decide what notification interval to use
	rand.Seed(time.Now().UnixNano())
	intv = rand.Intn(len(intvs))
	log.Printf("using interval set: %q\n", intvs[intv])
	// create tickers and launch monitor funcs
	pingticker := time.NewTicker(time.Second * intvs[intv]["ping"])
	go pingSvcs(wl.Svcs, pingticker.C)

	// mainloop
	keepalive := true
	for keepalive {
		select {
		case <-sigchan:
			// we've trapped a signal from the OS. tell our Asock to
			// shut down, but don't exit the eventloop because we want
			// to handle the Msgs which will be incoming.
			log.Println("OS signal received; shutting down")
			keepalive = false
		}
		// there's no default case in the select, as that would cause
		// it to be nonblocking. and that would cause main() to exit
		// immediately.
	}
}

func pingSvcs(svcs map[string]*whiplash.Svc, tc <-chan time.Time) {
	for _ = range tc {
		for _, svc := range svcs {
			svc.Ping()
			log.Println("sending ping request")
			sendData("ping", &whiplash.Request{Svc: svc.Core, Payload: nil})
		}
	}
}

func sendData(cmd string, r *whiplash.Request) {
	ac, err := aclient.NewTCP(*acconf)
	if err != nil {
		log.Println(err)
		return
	}
	defer ac.Close()
	jreq, err := json.Marshal(r)
	if err != nil {
		log.Println(err)
		return
	}
	req := []byte(cmd)
	req = append(req, 32)
	req = append(req, jreq...)
	resp, err := ac.Dispatch(req)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(string(resp))
}
<commit_msg>removing old planned global request<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/signal"
	"strconv"
	"syscall"
	"time"

	"firepear.net/aclient"
	"github.com/sboyettedh/whiplash"
)

var (
	whipconf string
	hostname string
	acconf *aclient.Config
	// which interval set to use for tickers
	intv int
	// the interval sets
	intvs = []map[string]time.Duration{
		{"ping": 11},
		{"ping": 13},
		{"ping": 17},
		{"ping": 19},
	}
)

func init() {
	flag.StringVar(&whipconf, "whipconf", "/etc/whiplash.json", "Whiplash configuration file")
	hostname, _ = os.LookupEnv("HOSTNAME")
}

func clientInit(fn string) (chan os.Signal, error) {
	// set up logfile
	f, err := os.Create("/var/log/" + fn + ".log")
	if err != nil {
		return nil, err
	}
	log.SetOutput(f)
	// write pidfile
	pidstr := strconv.Itoa(os.Getpid()) + "\n"
	err = ioutil.WriteFile("/var/run/" + fn + ".pid", []byte(pidstr), 0644)
	if err != nil {
		return nil, err
	}
	// and register SIGINT/SIGTERM handler
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)
	return sigchan, err
}

func main() {
	flag.Parse()
	sigchan, err := clientInit("whiplash-client")
	if err != nil{
		log.Fatal(err)
	}
	log.Printf("whiplash-client v%v beginning operations\n", whiplash.Version)

	wl, err := whiplash.New(whipconf, true)
	if err != nil {
		log.Printf("%v: could not read configuration file: %v\n", os.Args[0], err)
		os.Exit(1)
	}

	// need an aclient configuration to talk to the aggregator with
	acconf = &aclient.Config{
		Addr: wl.Aggregator.BindAddr + ":" + wl.Aggregator.BindPort,
		Timeout: 100,
	}

	// decide what notification interval to use
	rand.Seed(time.Now().UnixNano())
	intv = rand.Intn(len(intvs))
	log.Printf("using interval set: %q\n", intvs[intv])
	// create tickers and launch monitor funcs
	pingticker := time.NewTicker(time.Second * intvs[intv]["ping"])
	go pingSvcs(wl.Svcs, pingticker.C)

	// mainloop
	keepalive := true
	for keepalive {
		select {
		case <-sigchan:
			// we've trapped a signal from the OS. tell our Asock to
			// shut down, but don't exit the eventloop because we want
			// to handle the Msgs which will be incoming.
			log.Println("OS signal received; shutting down")
			keepalive = false
		}
		// there's no default case in the select, as that would cause
		// it to be nonblocking. and that would cause main() to exit
		// immediately.
	}
}

func pingSvcs(svcs map[string]*whiplash.Svc, tc <-chan time.Time) {
	for _ = range tc {
		for _, svc := range svcs {
			svc.Ping()
			log.Println("sending ping request")
			sendData("ping", &whiplash.Request{Svc: svc.Core, Payload: nil})
		}
	}
}

func sendData(cmd string, r *whiplash.Request) {
	ac, err := aclient.NewTCP(*acconf)
	if err != nil {
		log.Println(err)
		return
	}
	defer ac.Close()
	jreq, err := json.Marshal(r)
	if err != nil {
		log.Println(err)
		return
	}
	req := []byte(cmd)
	req = append(req, 32)
	req = append(req, jreq...)
	resp, err := ac.Dispatch(req)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println(string(resp))
}
<|endoftext|>


<commit_before>package kata_test
import (
  . "github.com/onsi/ginkgo"
  . "github.com/onsi/gomega"
  . "github.com/stevejaxon/learning-go-lang/codewars/kata"
)

var _ = Describe("Example tests", func() {
	It("Example test cases", func() {
		Expect(Play("100000000", "1000000")).To(Equal([2]string{"101000000", "0"}))
		Expect(Play("99991808", "1000000")).To(Equal([2]string{"97991808", "3000000"}))
		Expect(Play("16777215", "4000000")).To(Equal([2]string{"20777215", "0"}))
		Expect(Play("100000000", "100000000")).To(Equal([2]string{"100000000", "100000000"}))
		Expect(Play("429461545327902976", "592817564534231")).To(Equal([2]string{"428275910198834514", "1778452693602693"}))
		Expect(Play("4611686018427387903", "937836257282654172")).To(Equal([2]string{"5549522275710042075", "0"}))
		Expect(Play("19223372036854775808", "578721384725774464")).To(Equal([2]string{"18065929267403226880", "1736164154177323392"}))
		Expect(Play("9444732965739290427392", "1")).To(Equal([2]string{"9444732965739290427393", "0"}))
	})

	It("Edge case tests", func() {
		Expect(Play("0", "0")).To(Equal([2]string{"0", "0"}))
		Expect(Play("1", "0")).To(Equal([2]string{"1", "0"}))
		Expect(Play("0", "1")).To(Equal([2]string{"0", "1"}))
		Expect(Play("1", "1")).To(Equal([2]string{"1", "1"}))
		Expect(Play("3", "1")).To(Equal([2]string{"4", "0"}))
		Expect(Play("2", "1")).To(Equal([2]string{"3", "0"}))
		Expect(Play("5", "2")).To(Equal([2]string{"1", "6"}))
		Expect(Play("100000000", "50000001")).To(Equal([2]string{"100000000", "50000001"}))
		Expect(Play("9223372036854775807", "1")).To(Equal([2]string{"9223372036854775808", "0"}))
		Expect(Play("9223372034707292159", "1")).To(Equal([2]string{"9223372034707292157", "3"}))
	})
})<commit_msg>added random test cases<commit_after>package kata_test
import (
  . "github.com/onsi/ginkgo"
  . "github.com/onsi/gomega"
  . "github.com/stevejaxon/learning-go-lang/codewars/kata"
  "math/big"
  "math/rand"
)

var _ = Describe("Example tests", func() {
	It("Example test cases", func() {
		Expect(Play("100000000", "1000000")).To(Equal([2]string{"101000000", "0"}))
		Expect(Play("99991808", "1000000")).To(Equal([2]string{"97991808", "3000000"}))
		Expect(Play("16777215", "4000000")).To(Equal([2]string{"20777215", "0"}))
		Expect(Play("100000000", "100000000")).To(Equal([2]string{"100000000", "100000000"}))
		Expect(Play("429461545327902976", "592817564534231")).To(Equal([2]string{"428275910198834514", "1778452693602693"}))
		Expect(Play("4611686018427387903", "937836257282654172")).To(Equal([2]string{"5549522275710042075", "0"}))
		Expect(Play("19223372036854775808", "578721384725774464")).To(Equal([2]string{"18065929267403226880", "1736164154177323392"}))
		Expect(Play("9444732965739290427392", "1")).To(Equal([2]string{"9444732965739290427393", "0"}))
	})

	It("Edge case tests", func() {
		Expect(Play("0", "0")).To(Equal([2]string{"0", "0"}))
		Expect(Play("1", "0")).To(Equal([2]string{"1", "0"}))
		Expect(Play("0", "1")).To(Equal([2]string{"0", "1"}))
		Expect(Play("1", "1")).To(Equal([2]string{"1", "1"}))
		Expect(Play("3", "1")).To(Equal([2]string{"4", "0"}))
		Expect(Play("2", "1")).To(Equal([2]string{"3", "0"}))
		Expect(Play("5", "2")).To(Equal([2]string{"1", "6"}))
		Expect(Play("100000000", "50000001")).To(Equal([2]string{"100000000", "50000001"}))
		Expect(Play("9223372036854775807", "1")).To(Equal([2]string{"9223372036854775808", "0"}))
		Expect(Play("9223372034707292159", "1")).To(Equal([2]string{"9223372034707292157", "3"}))
	})

	It("Valid random test cases", func() {
		rnd := rand.New(rand.NewSource(42))
		for i := 0; i < 10; i++ {
			jackpot := getVeryLargeRandom(rnd)
			jackpotString := jackpot.String()
			gamble := getValidGambleAmount(rnd, jackpot).String()
			Expect(Play(jackpotString, gamble)).To(Equal(solution(jackpotString, gamble)))
		}
	})
})

func getVeryLargeRandom(rnd *rand.Rand) *big.Int {
	max, _ := new(big.Int).SetString("100000000000000000000000000", 10)
	return new(big.Int).Rand(rnd, max)
}

func getValidGambleAmount(rnd *rand.Rand, jackpot *big.Int) *big.Int {
	max := new(big.Int).Div(jackpot, big.NewInt(int64(2)))
	return new(big.Int).Rand(rnd, max)
}

func solution(reserves, gambled string) [2]string {
	amountInMachine, _ := new(big.Int).SetString(reserves, 10)
	amountGambled, _ := new(big.Int).SetString(gambled, 10)
	jackpot := new(big.Int).Mul(amountGambled, big.NewInt(int64(2)))
	if jackpot.Cmp(amountInMachine) > 0 {
		return [2]string{reserves, gambled}
	}
	if hasWon(amountInMachine) {
		remaining := new(big.Int).Sub(amountInMachine, jackpot)
		payout := new(big.Int).Add(amountGambled, jackpot)
		return [2]string{remaining.String(), payout.String()}
	}
	remaining := new(big.Int).Add(amountInMachine, amountGambled)
	return [2]string{remaining.String(), "0"}
}

func hasWon(amountInMachine *big.Int) bool {
	numBits := amountInMachine.BitLen()
	if numBits & 1 == 1 {
		return amountInMachine.Bit(numBits/2) == uint(0)
	}
	return false
}
<|endoftext|>


<commit_before>package testlib

import "fmt"

func (t *TestSuite) TestBasicCreationSequence() {
	// Check only after at least the first frame has been rendered
	<-t.testDraw

	exp := []string{"onCreate", "onStart", "onResume"}

	if a := t.Equal(
		len(exp),
		len(t.creationSequence),
		fmt.Sprintf("Triggered/Catched events were %v", t.creationSequence),
	); a.Passed {
		for i, exp := range []string{"onCreate", "onStart", "onResume"} {
			t.Equal(exp, t.creationSequence[i])
		}
	}
}

func (t *TestSuite) TestActionUpDown() {
	// Check only after at least the first frame has been rendered
	<-t.testDraw

	if err := Tap(100.0, 100.0); err != nil {
		panic(err)
	}

	event := <-t.testActionUpDown

	t.True(event.Down)
	t.Equal(float32(100.0), event.X)
	t.Equal(float32(100.0), event.Y)
}

func (t *TestSuite) TestActionMove() {
	// Check only after at least the first frame has been rendered
	<-t.testDraw

	// Move the cursor on the initial position (this has no effect
	// on the android-side but it's necessary during the xorg
	// test)
	if err := Move(11, 11, 11, 11); err != nil {
		panic(err)
	}

	movements := 10

	// Begin counting move events from now
	t.resetActionMove <- movements
	<-t.resetActionMove

	if err := Move(10, 10, 20, 20); err != nil {
		panic(err)
	}

	close(t.testActionMove)

	for event := range t.testActionMove {
		t.True(event.X != 0.0)
		t.True(event.Y != 0.0)
	}

	t.moving = false
}

func (t *TestSuite) TestDraw() {
	t.True(<-t.testDraw)
}

// func (t *TestSuite) TestBasicExitSequence() {
// 	if err := Back(); err != nil {
// 		panic(err)
// 	}
// 	<-t.testPause
// 	exp := []string{"onPause"}

// 	if a := t.Equal(
// 		len(exp),
// 		len(t.creationSequence),
// 		fmt.Sprintf("Triggered/Catched events were %v", t.exitSequence),
// 	); a.Passed {
// 		for i, exp := range []string{"onPause"} {
// 			t.Equal(exp, t.creationSequence[i])
// 		}
// 	}
// }
<commit_msg>Minor changes in TestActionMove<commit_after>package testlib

import "fmt"

func (t *TestSuite) TestBasicCreationSequence() {
	// Check only after at least the first frame has been rendered
	<-t.testDraw

	exp := []string{"onCreate", "onStart", "onResume"}

	if a := t.Equal(
		len(exp),
		len(t.creationSequence),
		fmt.Sprintf("Triggered/Catched events were %v", t.creationSequence),
	); a.Passed {
		for i, exp := range []string{"onCreate", "onStart", "onResume"} {
			t.Equal(exp, t.creationSequence[i])
		}
	}
}

func (t *TestSuite) TestActionUpDown() {
	// Check only after at least the first frame has been rendered
	<-t.testDraw

	if err := Tap(100.0, 100.0); err != nil {
		panic(err)
	}

	event := <-t.testActionUpDown

	t.True(event.Down)
	t.Equal(float32(100.0), event.X)
	t.Equal(float32(100.0), event.Y)
}

func (t *TestSuite) TestActionMove() {
	// Check only after at least the first frame has been rendered
	<-t.testDraw

	// Move the cursor on the initial position (this has no effect
	// on the android-side but it's necessary during the xorg
	// test)
	if err := Move(11, 11, 11, 11); err != nil {
		panic(err)
	}

	movements := 10

	// Begin counting move events from now
	t.resetActionMove <- movements
	<-t.resetActionMove

	if err := Move(10, 10, 20, 20); err != nil {
		panic(err)
	}

	close(t.testActionMove)

	for event := range t.testActionMove {
		t.True(event.X > 0.0)
		t.True(event.Y > 0.0)
	}

	t.moving = false
}

func (t *TestSuite) TestDraw() {
	t.True(<-t.testDraw)
}

// func (t *TestSuite) TestBasicExitSequence() {
// 	if err := Back(); err != nil {
// 		panic(err)
// 	}
// 	<-t.testPause
// 	exp := []string{"onPause"}

// 	if a := t.Equal(
// 		len(exp),
// 		len(t.creationSequence),
// 		fmt.Sprintf("Triggered/Catched events were %v", t.exitSequence),
// 	); a.Passed {
// 		for i, exp := range []string{"onPause"} {
// 			t.Equal(exp, t.creationSequence[i])
// 		}
// 	}
// }
<|endoftext|>


<commit_before>package gads

import (
	"encoding/xml"
)

// A campaignService holds the connection information for the
// campaign service.
type CampaignService struct {
	Auth
}

// NewCampaignService creates a new campaignService
func NewCampaignService(auth *Auth) *CampaignService {
	return &CampaignService{Auth: *auth}
}

// ConversionOptimizerEligibility
//
// RejectionReasons can be any of
//   "CAMPAIGN_IS_NOT_ACTIVE", "NOT_CPC_CAMPAIGN","CONVERSION_TRACKING_NOT_ENABLED",
//   "NOT_ENOUGH_CONVERSIONS", "UNKNOWN"
//
type conversionOptimizerEligibility struct {
	Eligible         bool     `xml:"eligible"`         // is eligible for optimization
	RejectionReasons []string `xml:"rejectionReasons"` // reason for why campaign is
	// not eligible for conversion optimization.
}

type FrequencyCap struct {
	Impressions int64  `xml:"impressions"`
	TimeUnit    string `xml:"timeUnit"`
	Level       string `xml:"level,omitempty"`
}

type CampaignSetting struct {
	XMLName xml.Name `xml:"settings"`
	Type    string   `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`

	// GeoTargetTypeSetting
	PositiveGeoTargetType *string `xml:"positiveGeoTargetType,omitempty"`
	NegativeGeoTargetType *string `xml:"negativeGeoTargetType,omitempty"`

	// RealTimeBiddingSetting
	OptIn *bool `xml:"optIn,omitempty"`

	// DynamicSearchAdsSetting
	DomainName   *string `xml:"domainName,omitempty"`
	LanguageCode *string `xml:"langaugeCode,omitempty"`

	// TrackingSetting
	TrackingUrl *string `xml:"trackingUrl,omitempty"`
}

func NewDynamicSearchAdsSetting(domainName, languageCode string) CampaignSetting {
	return CampaignSetting{
		Type:         "DynamicSearchAdsSetting",
		DomainName:   &domainName,
		LanguageCode: &languageCode,
	}
}

func NewGeoTargetTypeSetting(positiveGeoTargetType, negativeGeoTargetType string) CampaignSetting {
	return CampaignSetting{
		Type: "GeoTargetTypeSetting",
		PositiveGeoTargetType: &positiveGeoTargetType,
		NegativeGeoTargetType: &negativeGeoTargetType,
	}
}

func NewRealTimeBiddingSetting(optIn bool) CampaignSetting {
	return CampaignSetting{
		Type:  "RealTimeBiddingSetting",
		OptIn: &optIn,
	}
}

func NewTrackingSetting(trackingUrl string) CampaignSetting {
	return CampaignSetting{
		Type:        "TrackingSetting",
		TrackingUrl: &trackingUrl,
	}
}

type NetworkSetting struct {
	TargetGoogleSearch         bool `xml:"targetGoogleSearch"`
	TargetSearchNetwork        bool `xml:"targetSearchNetwork"`
	TargetContentNetwork       bool `xml:"targetContentNetwork"`
	TargetPartnerSearchNetwork bool `xml:"targetPartnerSearchNetwork"`
}

type BiddingScheme struct {
	Type               string `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
	EnhancedCpcEnabled bool   `xml:"enhancedCpcEnabled"`
}

type Bid struct {
	Type         string  `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
	Amount       int64 `xml:"bid>microAmount"`
	CpcBidSource *string `xml:"cpcBidSource,omitempty"`
	CpmBidSource *string `xml:"cpmBidSource,omitempty"`
}

type BiddingStrategyConfiguration struct {
	StrategyId     int64          `xml:"biddingStrategyId,omitempty"`
	StrategyName   string         `xml:"biddingStrategyName,omitempty"`
	StrategyType   string         `xml:"biddingStrategyType,omitempty"`
	StrategySource string         `xml:"biddingStrategySource,omitempty"`
	Scheme         *BiddingScheme `xml:"biddingScheme,omitempty"`
	Bids           []Bid          `xml:"bids"`
}

type CustomParameter struct {
	Key      string `xml:"key"`
	Value    string `xml:"value"`
	IsRemove bool   `xml:"isRemove"`
}

type CustomParameters struct {
	CustomParameters []CustomParameter `xml:"parameters"`
	DoReplace        bool              `xml:"doReplace"`
}

type Campaign struct {
	Id                             int64                           `xml:"id,omitempty"`
	Name                           string                          `xml:"name"`
	Status                         string                          `xml:"status"`                  // Status: "ENABLED", "PAUSED", "REMOVED"
	ServingStatus                  *string                         `xml:"servingStatus,omitempty"` // ServingStatus: "SERVING", "NONE", "ENDED", "PENDING", "SUSPENDED"
	StartDate                      string                          `xml:"startDate"`
	EndDate                        *string                         `xml:"endDate,omitempty"`
	Budget                         *Budget                         `xml:"budget"`
	ConversionOptimizerEligibility *conversionOptimizerEligibility `xml:"conversionOptimizerEligibility"`
	AdServingOptimizationStatus    string                          `xml:"adServingOptimizationStatus,omitempty"`
	FrequencyCap                   *FrequencyCap                   `xml:"frequencyCap"`
	Settings                       []CampaignSetting               `xml:"settings"`
	AdvertisingChannelType         string                          `xml:"advertisingChannelType,omitempty"`    // "UNKNOWN", "SEARCH", "DISPLAY", "SHOPPING"
	AdvertisingChannelSubType      *string                         `xml:"advertisingChannelSubType,omitempty"` // "UNKNOWN", "SEARCH_MOBILE_APP", "DISPLAY_MOBILE_APP", "SEARCH_EXPRESS", "DISPLAY_EXPRESS"
	NetworkSetting                 *NetworkSetting                 `xml:"networkSetting"`
	Labels                         []Label                         `xml:"labels"`
	BiddingStrategyConfiguration   *BiddingStrategyConfiguration   `xml:"biddingStrategyConfiguration"`
	ForwardCompatibilityMap        *map[string]string              `xml:"forwardCompatibilityMap,omitempty"`
	TrackingUrlTemplate            *string                         `xml:"trackingUrlTemplate"`
	UrlCustomParameters            *CustomParameters               `xml:"urlCustomParametes"`
	Errors                         []error                         `xml:"-"`
}

type CampaignOperations map[string][]Campaign

type CampaignLabel struct {
	CampaignId int64 `xml:"campaignId"`
	LabelId    int64 `xml:"labelId"`
}

type CampaignLabelOperations map[string][]CampaignLabel

// Get returns an array of Campaign's and the total number of campaign's matching
// the selector.
//
// Example
//
//   campaigns, totalCount, err := campaignService.Get(
//     gads.Selector{
//       Fields: []string{
//         "AdGroupId",
//         "Status",
//         "AdGroupCreativeApprovalStatus",
//         "AdGroupAdDisapprovalReasons",
//         "AdGroupAdTrademarkDisapproved",
//       },
//       Predicates: []gads.Predicate{
//         {"AdGroupId", "EQUALS", []string{adGroupId}},
//       },
//     },
//   )
//
// Selectable fields are
//   "Id", "Name", "Status", "ServingStatus", "StartDate", "EndDate", "AdServingOptimizationStatus",
//   "Settings", "AdvertisingChannelType", "AdvertisingChannelSubType", "Labels", "TrackingUrlTemplate",
//   "UrlCustomParameters"
//
// filterable fields are
//   "Id", "Name", "Status", "ServingStatus", "StartDate", "EndDate", "AdvertisingChannelType",
//   "AdvertisingChannelSubType", "Labels", "TrackingUrlTemplate"
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#get
//
func (s *CampaignService) Get(selector Selector) (campaigns []Campaign, totalCount int64, err error) {
	selector.XMLName = xml.Name{"", "serviceSelector"}
	respBody, err := s.Auth.request(
		campaignServiceUrl,
		"get",
		struct {
			XMLName xml.Name
			Sel     Selector
		}{
			XMLName: xml.Name{
				Space: baseUrl,
				Local: "get",
			},
			Sel: selector,
		},
	)
	if err != nil {
		return campaigns, totalCount, err
	}
	getResp := struct {
		Size      int64      `xml:"rval>totalNumEntries"`
		Campaigns []Campaign `xml:"rval>entries"`
	}{}
	err = xml.Unmarshal([]byte(respBody), &getResp)
	if err != nil {
		return campaigns, totalCount, err
	}
	return getResp.Campaigns, getResp.Size, err
}

// Mutate allows you to add and modify campaigns, returning the
// campaigns.  Note that the "REMOVE" operator is not supported.
// To remove a campaign set its Status to "REMOVED".
//
// Example
//
//  campaignNeedingRemoval.Status = "REMOVED"
//  ads, err := campaignService.Mutate(
//    gads.CampaignOperations{
//      "ADD": {
//        gads.Campaign{
//          Name: "my campaign name",
//          Status: "PAUSED",
//          StartDate: time.Now().Format("20060102"),
//          BudgetId: 321543214,
//          AdServingOptimizationStatus: "ROTATE_INDEFINITELY",
//          Settings: []gads.CampaignSetting{
//            gads.NewRealTimeBiddingSetting(true),
//          },
//          AdvertisingChannelType: "SEARCH",
//          BiddingStrategyConfiguration: &gads.BiddingStrategyConfiguration{
//            StrategyType: "MANUAL_CPC",
//          },
//        },
//        campaignNeedingRemoval,
//      },
//      "SET": {
//        modifiedCampaign,
//      },
//    }
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#mutate
//
func (s *CampaignService) Mutate(campaignOperations CampaignOperations) (campaigns []Campaign, err error) {
	type campaignOperation struct {
		Action   string   `xml:"operator"`
		Campaign Campaign `xml:"operand"`
	}
	operations := []campaignOperation{}
	for action, campaigns := range campaignOperations {
		for _, campaign := range campaigns {
			operations = append(operations,
				campaignOperation{
					Action:   action,
					Campaign: campaign,
				},
			)
		}
	}
	mutation := struct {
		XMLName xml.Name
		Ops     []campaignOperation `xml:"operations"`
	}{
		XMLName: xml.Name{
			Space: baseUrl,
			Local: "mutate",
		},
		Ops: operations}
	respBody, err := s.Auth.request(campaignServiceUrl, "mutate", mutation)
	if err != nil {
		return campaigns, err
	}
	mutateResp := struct {
		Campaigns []Campaign `xml:"rval>value"`
	}{}
	err = xml.Unmarshal([]byte(respBody), &mutateResp)
	if err != nil {
		return campaigns, err
	}

	return mutateResp.Campaigns, err
}

// Mutate allows you to add and removes labels from campaigns.
//
// Example
//
//  cls, err := campaignService.MutateLabel(
//    gads.CampaignOperations{
//      "ADD": {
//        gads.CampaignLabel{CampaignId: 3200, LabelId: 5353},
//        gads.CampaignLabel{CampaignId: 4320, LabelId: 5643},
//      },
//      "REMOVE": {
//        gads.CampaignLabel{CampaignId: 3653, LabelId: 5653},
//      },
//    }
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#mutateLabel
//
func (s *CampaignService) MutateLabel(campaignLabelOperations CampaignLabelOperations) (campaignLabels []CampaignLabel, err error) {
	type campaignLabelOperation struct {
		Action        string        `xml:"operator"`
		CampaignLabel CampaignLabel `xml:"operand"`
	}
	operations := []campaignLabelOperation{}
	for action, campaignLabels := range campaignLabelOperations {
		for _, campaignLabel := range campaignLabels {
			operations = append(operations,
				campaignLabelOperation{
					Action:        action,
					CampaignLabel: campaignLabel,
				},
			)
		}
	}
	mutation := struct {
		XMLName xml.Name
		Ops     []campaignLabelOperation `xml:"operations"`
	}{
		XMLName: xml.Name{
			Space: baseUrl,
			Local: "mutateLabel",
		},
		Ops: operations}
	respBody, err := s.Auth.request(campaignServiceUrl, "mutateLabel", mutation)
	if err != nil {
		return campaignLabels, err
	}
	mutateResp := struct {
		CampaignLabels []CampaignLabel `xml:"rval>value"`
	}{}
	err = xml.Unmarshal([]byte(respBody), &mutateResp)
	if err != nil {
		return campaignLabels, err
	}

	return mutateResp.CampaignLabels, err
}

// Query is not yet implemented
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#query
//
func (s *CampaignService) Query(query string) (campaigns []Campaign, totalCount int64, err error) {
	return campaigns, totalCount, ERROR_NOT_YET_IMPLEMENTED
}
<commit_msg>All filed of campaign struct marked as omitifempty, to be able make partial updates of campaign<commit_after>package gads

import (
	"encoding/xml"
)

// A campaignService holds the connection information for the
// campaign service.
type CampaignService struct {
	Auth
}

// NewCampaignService creates a new campaignService
func NewCampaignService(auth *Auth) *CampaignService {
	return &CampaignService{Auth: *auth}
}

// ConversionOptimizerEligibility
//
// RejectionReasons can be any of
//   "CAMPAIGN_IS_NOT_ACTIVE", "NOT_CPC_CAMPAIGN","CONVERSION_TRACKING_NOT_ENABLED",
//   "NOT_ENOUGH_CONVERSIONS", "UNKNOWN"
//
type conversionOptimizerEligibility struct {
	Eligible         bool     `xml:"eligible"`         // is eligible for optimization
	RejectionReasons []string `xml:"rejectionReasons"` // reason for why campaign is
	// not eligible for conversion optimization.
}

type FrequencyCap struct {
	Impressions int64  `xml:"impressions"`
	TimeUnit    string `xml:"timeUnit"`
	Level       string `xml:"level,omitempty"`
}

type CampaignSetting struct {
	XMLName xml.Name `xml:"settings"`
	Type    string   `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`

	// GeoTargetTypeSetting
	PositiveGeoTargetType *string `xml:"positiveGeoTargetType,omitempty"`
	NegativeGeoTargetType *string `xml:"negativeGeoTargetType,omitempty"`

	// RealTimeBiddingSetting
	OptIn *bool `xml:"optIn,omitempty"`

	// DynamicSearchAdsSetting
	DomainName   *string `xml:"domainName,omitempty"`
	LanguageCode *string `xml:"langaugeCode,omitempty"`

	// TrackingSetting
	TrackingUrl *string `xml:"trackingUrl,omitempty"`
}

func NewDynamicSearchAdsSetting(domainName, languageCode string) CampaignSetting {
	return CampaignSetting{
		Type:         "DynamicSearchAdsSetting",
		DomainName:   &domainName,
		LanguageCode: &languageCode,
	}
}

func NewGeoTargetTypeSetting(positiveGeoTargetType, negativeGeoTargetType string) CampaignSetting {
	return CampaignSetting{
		Type: "GeoTargetTypeSetting",
		PositiveGeoTargetType: &positiveGeoTargetType,
		NegativeGeoTargetType: &negativeGeoTargetType,
	}
}

func NewRealTimeBiddingSetting(optIn bool) CampaignSetting {
	return CampaignSetting{
		Type:  "RealTimeBiddingSetting",
		OptIn: &optIn,
	}
}

func NewTrackingSetting(trackingUrl string) CampaignSetting {
	return CampaignSetting{
		Type:        "TrackingSetting",
		TrackingUrl: &trackingUrl,
	}
}

type NetworkSetting struct {
	TargetGoogleSearch         bool `xml:"targetGoogleSearch"`
	TargetSearchNetwork        bool `xml:"targetSearchNetwork"`
	TargetContentNetwork       bool `xml:"targetContentNetwork"`
	TargetPartnerSearchNetwork bool `xml:"targetPartnerSearchNetwork"`
}

type BiddingScheme struct {
	Type               string `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
	EnhancedCpcEnabled bool   `xml:"enhancedCpcEnabled"`
}

type Bid struct {
	Type         string  `xml:"http://www.w3.org/2001/XMLSchema-instance type,attr"`
	Amount       int64 `xml:"bid>microAmount"`
	CpcBidSource *string `xml:"cpcBidSource,omitempty"`
	CpmBidSource *string `xml:"cpmBidSource,omitempty"`
}

type BiddingStrategyConfiguration struct {
	StrategyId     int64          `xml:"biddingStrategyId,omitempty"`
	StrategyName   string         `xml:"biddingStrategyName,omitempty"`
	StrategyType   string         `xml:"biddingStrategyType,omitempty"`
	StrategySource string         `xml:"biddingStrategySource,omitempty"`
	Scheme         *BiddingScheme `xml:"biddingScheme,omitempty"`
	Bids           []Bid          `xml:"bids"`
}

type CustomParameter struct {
	Key      string `xml:"key"`
	Value    string `xml:"value"`
	IsRemove bool   `xml:"isRemove"`
}

type CustomParameters struct {
	CustomParameters []CustomParameter `xml:"parameters"`
	DoReplace        bool              `xml:"doReplace"`
}

type Campaign struct {
	Id                             int64                           `xml:"id,omitempty"`
	Name                           string                          `xml:"name,omitempty"`
	Status                         string                          `xml:"status,omitempty"`                  // Status: "ENABLED", "PAUSED", "REMOVED"
	ServingStatus                  *string                         `xml:"servingStatus,omitempty"` // ServingStatus: "SERVING", "NONE", "ENDED", "PENDING", "SUSPENDED"
	StartDate                      string                          `xml:"startDate,omitempty"`
	EndDate                        *string                         `xml:"endDate,omitempty"`
	Budget                         *Budget                         `xml:"budget,omitempty"`
	ConversionOptimizerEligibility *conversionOptimizerEligibility `xml:"conversionOptimizerEligibility,omitempty"`
	AdServingOptimizationStatus    string                          `xml:"adServingOptimizationStatus,omitempty"`
	FrequencyCap                   *FrequencyCap                   `xml:"frequencyCap,omitempty"`
	Settings                       []CampaignSetting               `xml:"settings,omitempty"`
	AdvertisingChannelType         string                          `xml:"advertisingChannelType,omitempty"`    // "UNKNOWN", "SEARCH", "DISPLAY", "SHOPPING"
	AdvertisingChannelSubType      *string                         `xml:"advertisingChannelSubType,omitempty"` // "UNKNOWN", "SEARCH_MOBILE_APP", "DISPLAY_MOBILE_APP", "SEARCH_EXPRESS", "DISPLAY_EXPRESS"
	NetworkSetting                 *NetworkSetting                 `xml:"networkSetting,omitempty"`
	Labels                         []Label                         `xml:"labels,omitempty"`
	BiddingStrategyConfiguration   *BiddingStrategyConfiguration   `xml:"biddingStrategyConfiguration,omitempty"`
	ForwardCompatibilityMap        *map[string]string              `xml:"forwardCompatibilityMap,omitempty"`
	TrackingUrlTemplate            *string                         `xml:"trackingUrlTemplat,omitemptye"`
	UrlCustomParameters            *CustomParameters               `xml:"urlCustomParametes,omitempty"`
	Errors                         []error                         `xml:"-"`
}

type CampaignOperations map[string][]Campaign

type CampaignLabel struct {
	CampaignId int64 `xml:"campaignId"`
	LabelId    int64 `xml:"labelId"`
}

type CampaignLabelOperations map[string][]CampaignLabel

// Get returns an array of Campaign's and the total number of campaign's matching
// the selector.
//
// Example
//
//   campaigns, totalCount, err := campaignService.Get(
//     gads.Selector{
//       Fields: []string{
//         "AdGroupId",
//         "Status",
//         "AdGroupCreativeApprovalStatus",
//         "AdGroupAdDisapprovalReasons",
//         "AdGroupAdTrademarkDisapproved",
//       },
//       Predicates: []gads.Predicate{
//         {"AdGroupId", "EQUALS", []string{adGroupId}},
//       },
//     },
//   )
//
// Selectable fields are
//   "Id", "Name", "Status", "ServingStatus", "StartDate", "EndDate", "AdServingOptimizationStatus",
//   "Settings", "AdvertisingChannelType", "AdvertisingChannelSubType", "Labels", "TrackingUrlTemplate",
//   "UrlCustomParameters"
//
// filterable fields are
//   "Id", "Name", "Status", "ServingStatus", "StartDate", "EndDate", "AdvertisingChannelType",
//   "AdvertisingChannelSubType", "Labels", "TrackingUrlTemplate"
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#get
//
func (s *CampaignService) Get(selector Selector) (campaigns []Campaign, totalCount int64, err error) {
	selector.XMLName = xml.Name{"", "serviceSelector"}
	respBody, err := s.Auth.request(
		campaignServiceUrl,
		"get",
		struct {
			XMLName xml.Name
			Sel     Selector
		}{
			XMLName: xml.Name{
				Space: baseUrl,
				Local: "get",
			},
			Sel: selector,
		},
	)
	if err != nil {
		return campaigns, totalCount, err
	}
	getResp := struct {
		Size      int64      `xml:"rval>totalNumEntries"`
		Campaigns []Campaign `xml:"rval>entries"`
	}{}
	err = xml.Unmarshal([]byte(respBody), &getResp)
	if err != nil {
		return campaigns, totalCount, err
	}
	return getResp.Campaigns, getResp.Size, err
}

// Mutate allows you to add and modify campaigns, returning the
// campaigns.  Note that the "REMOVE" operator is not supported.
// To remove a campaign set its Status to "REMOVED".
//
// Example
//
//  campaignNeedingRemoval.Status = "REMOVED"
//  ads, err := campaignService.Mutate(
//    gads.CampaignOperations{
//      "ADD": {
//        gads.Campaign{
//          Name: "my campaign name",
//          Status: "PAUSED",
//          StartDate: time.Now().Format("20060102"),
//          BudgetId: 321543214,
//          AdServingOptimizationStatus: "ROTATE_INDEFINITELY",
//          Settings: []gads.CampaignSetting{
//            gads.NewRealTimeBiddingSetting(true),
//          },
//          AdvertisingChannelType: "SEARCH",
//          BiddingStrategyConfiguration: &gads.BiddingStrategyConfiguration{
//            StrategyType: "MANUAL_CPC",
//          },
//        },
//        campaignNeedingRemoval,
//      },
//      "SET": {
//        modifiedCampaign,
//      },
//    }
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#mutate
//
func (s *CampaignService) Mutate(campaignOperations CampaignOperations) (campaigns []Campaign, err error) {
	type campaignOperation struct {
		Action   string   `xml:"operator"`
		Campaign Campaign `xml:"operand"`
	}
	operations := []campaignOperation{}
	for action, campaigns := range campaignOperations {
		for _, campaign := range campaigns {
			operations = append(operations,
				campaignOperation{
					Action:   action,
					Campaign: campaign,
				},
			)
		}
	}
	mutation := struct {
		XMLName xml.Name
		Ops     []campaignOperation `xml:"operations"`
	}{
		XMLName: xml.Name{
			Space: baseUrl,
			Local: "mutate",
		},
		Ops: operations}
	respBody, err := s.Auth.request(campaignServiceUrl, "mutate", mutation)
	if err != nil {
		return campaigns, err
	}
	mutateResp := struct {
		Campaigns []Campaign `xml:"rval>value"`
	}{}
	err = xml.Unmarshal([]byte(respBody), &mutateResp)
	if err != nil {
		return campaigns, err
	}

	return mutateResp.Campaigns, err
}

// Mutate allows you to add and removes labels from campaigns.
//
// Example
//
//  cls, err := campaignService.MutateLabel(
//    gads.CampaignOperations{
//      "ADD": {
//        gads.CampaignLabel{CampaignId: 3200, LabelId: 5353},
//        gads.CampaignLabel{CampaignId: 4320, LabelId: 5643},
//      },
//      "REMOVE": {
//        gads.CampaignLabel{CampaignId: 3653, LabelId: 5653},
//      },
//    }
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#mutateLabel
//
func (s *CampaignService) MutateLabel(campaignLabelOperations CampaignLabelOperations) (campaignLabels []CampaignLabel, err error) {
	type campaignLabelOperation struct {
		Action        string        `xml:"operator"`
		CampaignLabel CampaignLabel `xml:"operand"`
	}
	operations := []campaignLabelOperation{}
	for action, campaignLabels := range campaignLabelOperations {
		for _, campaignLabel := range campaignLabels {
			operations = append(operations,
				campaignLabelOperation{
					Action:        action,
					CampaignLabel: campaignLabel,
				},
			)
		}
	}
	mutation := struct {
		XMLName xml.Name
		Ops     []campaignLabelOperation `xml:"operations"`
	}{
		XMLName: xml.Name{
			Space: baseUrl,
			Local: "mutateLabel",
		},
		Ops: operations}
	respBody, err := s.Auth.request(campaignServiceUrl, "mutateLabel", mutation)
	if err != nil {
		return campaignLabels, err
	}
	mutateResp := struct {
		CampaignLabels []CampaignLabel `xml:"rval>value"`
	}{}
	err = xml.Unmarshal([]byte(respBody), &mutateResp)
	if err != nil {
		return campaignLabels, err
	}

	return mutateResp.CampaignLabels, err
}

// Query is not yet implemented
//
// Relevant documentation
//
//     https://developers.google.com/adwords/api/docs/reference/v201409/CampaignService#query
//
func (s *CampaignService) Query(query string) (campaigns []Campaign, totalCount int64, err error) {
	return campaigns, totalCount, ERROR_NOT_YET_IMPLEMENTED
}
<|endoftext|>


<commit_before>package tests

import (
	"fmt"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/portworx/torpedo/drivers/node"
	"github.com/portworx/torpedo/drivers/scheduler"
	. "github.com/portworx/torpedo/tests"
)

func TestBasic(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Torpedo : Basic")
}

var _ = BeforeSuite(func() {
	InitInstance()
})

// This test performs basic test of starting an application and destroying it (along with storage)
var _ = Describe("{SetupTeardown}", func() {
	It("has to setup, validate and teardown apps", func() {
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("setupteardown-%d", i))...)
		}

		opts := make(map[string]bool)
		opts[scheduler.OptionsWaitForResourceLeakCleanup] = true

		for _, ctx := range contexts {
			TearDownContext(ctx, opts)
		}
	})
})

// Volume Driver Plugin is down, unavailable - and the client container should not be impacted.
var _ = Describe("{VolumeDriverDown}", func() {
	It("has to schedule apps and stop volume driver on app nodes", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldriverdown-%d", i))...)
		}

		Step("get nodes for all apps in test and bounce volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node
				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					appNodes, err = Inst().S.GetNodesForApp(ctx)
					Expect(err).NotTo(HaveOccurred())
					Expect(appNodes).NotTo(BeEmpty())
				})

				Step(
					fmt.Sprintf("stop volume driver %s on app %s's nodes: %v",
						Inst().V.String(), ctx.App.Key, appNodes),
					func() {
						StopVolDriverAndWait(appNodes)
					})

				Step("starting volume driver", func() {
					StartVolDriverAndWait(appNodes)
				})

				Step("Giving few seconds for volume driver to stabilize", func() {
					time.Sleep(20 * time.Second)
				})
			}
		})

		Step("destroy apps", func() {
			opts := make(map[string]bool)
			opts[scheduler.OptionsWaitForResourceLeakCleanup] = true
			for _, ctx := range contexts {
				TearDownContext(ctx, opts)
			}
		})

	})
})

// Volume Driver Plugin is down, unavailable on the nodes where the volumes are
// attached - and the client container should not be impacted.
var _ = Describe("{VolumeDriverDownAttachedNode}", func() {
	It("has to schedule apps and stop volume driver on nodes where volumes are attached", func() {
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldriverdownattachednode-%d", i))...)
		}

		Step("get nodes for all apps in test and restart volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node

				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					volumes, err := Inst().S.GetVolumes(ctx)
					Expect(err).NotTo(HaveOccurred())

					nodeMap := make(map[string]struct{})
					for _, v := range volumes {
						n, err := Inst().V.GetNodeForVolume(v)
						Expect(err).NotTo(HaveOccurred())

						if n == nil {
							continue
						}

						if _, exists := nodeMap[n.Name]; !exists {
							nodeMap[n.Name] = struct{}{}
							appNodes = append(appNodes, *n)
						}
					}
				})

				Step(fmt.Sprintf("stop volume driver %s on app %s's nodes: %v",
					Inst().V.String(), ctx.App.Key, appNodes), func() {
					StopVolDriverAndWait(appNodes)
				})

				Step("starting volume driver", func() {
					StartVolDriverAndWait(appNodes)
				})

				Step("Giving few seconds for volume driver to stabilize", func() {
					time.Sleep(20 * time.Second)
				})
			}
		})

		opts := make(map[string]bool)
		opts[scheduler.OptionsWaitForResourceLeakCleanup] = true
		ValidateAndDestroy(contexts, opts)
	})
})

// Volume Driver Plugin has crashed - and the client container should not be impacted.
var _ = Describe("{VolumeDriverCrash}", func() {
	It("has to schedule apps and crash volume driver on app nodes", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldrivercrash-%d", i))...)
		}

		Step("get nodes for all apps in test and crash volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node
				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					appNodes, err = Inst().S.GetNodesForApp(ctx)
					Expect(err).NotTo(HaveOccurred())
					Expect(appNodes).NotTo(BeEmpty())
				})

				Step(
					fmt.Sprintf("crash volume driver %s on app %s's nodes: %v",
						Inst().V.String(), ctx.App.Key, appNodes),
					func() {
						CrashVolDriverAndWait(appNodes)
					})
			}
		})

		opts := make(map[string]bool)
		opts[scheduler.OptionsWaitForResourceLeakCleanup] = true
		ValidateAndDestroy(contexts, opts)
	})
})

// Volume driver plugin is down and the client container gets terminated.
// There is a lost unmount call in this case. When the volume driver is
// back up, we should be able to detach and delete the volume.
var _ = Describe("{VolumeDriverAppDown}", func() {
	It("has to schedule apps, stop volume driver on app nodes and destroy apps", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldriverappdown-%d", i))...)
		}

		Step("get nodes for all apps in test and bounce volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node
				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					appNodes, err = Inst().S.GetNodesForApp(ctx)
					Expect(err).NotTo(HaveOccurred())
					Expect(appNodes).NotTo(BeEmpty())
				})

				Step(fmt.Sprintf("stop volume driver %s on app %s's nodes: %v",
					Inst().V.String(), ctx.App.Key, appNodes), func() {
					StopVolDriverAndWait(appNodes)
				})

				Step(fmt.Sprintf("destroy app: %s", ctx.App.Key), func() {
					err = Inst().S.Destroy(ctx, nil)
					Expect(err).NotTo(HaveOccurred())

					Step("wait for few seconds for app destroy to trigger", func() {
						time.Sleep(10 * time.Second)
					})
				})

				Step("restarting volume driver", func() {
					StartVolDriverAndWait(appNodes)
				})

				Step(fmt.Sprintf("wait for destroy of app: %s", ctx.App.Key), func() {
					err = Inst().S.WaitForDestroy(ctx)
					Expect(err).NotTo(HaveOccurred())
				})

				DeleteVolumesAndWait(ctx)
			}
		})
	})
})

// This test deletes all tasks of an application and checks if app converges back to desired state
var _ = Describe("{AppTasksDown}", func() {
	It("has to schedule app and delete app tasks", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("apptasksdown-%d", i))...)
		}

		Step("delete all application tasks", func() {
			for _, ctx := range contexts {
				Step(fmt.Sprintf("delete tasks for app: %s", ctx.App.Key), func() {
					err = Inst().S.DeleteTasks(ctx)
					Expect(err).NotTo(HaveOccurred())
				})

				ValidateContext(ctx)
			}
		})

		Step("teardown all apps", func() {
			for _, ctx := range contexts {
				TearDownContext(ctx, nil)
			}
		})
	})
})

// This test scales up and down an application and checks if app has actually scaled accordingly
var _ = Describe("{AppScaleUpAndDown}", func() {
	It("has to scale up and scale down the app", func() {
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("applicationscaleupdown-%d", i))...)
		}

		Step("scale up all applications", func() {
			for _, ctx := range contexts {
				Step(fmt.Sprintf("updating scale for app: %s by %d ", ctx.App.Key, len(node.GetWorkerNodes())), func() {
					applicationScaleUpMap, err := Inst().S.GetScaleFactorMap(ctx)
					Expect(err).NotTo(HaveOccurred())
					for name, scale := range applicationScaleUpMap {
						applicationScaleUpMap[name] = scale + int32(len(node.GetWorkerNodes()))
					}
					err = Inst().S.ScaleApplication(ctx, applicationScaleUpMap)
					Expect(err).NotTo(HaveOccurred())
				})

				ValidateContext(ctx)
			}
		})
		Step("scale down all applications", func() {
			for _, ctx := range contexts {
				Step(fmt.Sprintf("scale down app %s by 1", ctx.App.Key), func() {
					applicationScaleDownMap, err := Inst().S.GetScaleFactorMap(ctx)
					Expect(err).NotTo(HaveOccurred())
					for name, scale := range applicationScaleDownMap {
						applicationScaleDownMap[name] = scale - 1
					}
					err = Inst().S.ScaleApplication(ctx, applicationScaleDownMap)
					Expect(err).NotTo(HaveOccurred())
				})

			}
			Step("Giving few seconds for scaled applications to stabilize", func() {
				time.Sleep(10 * time.Second)
			})
			for _, ctx := range contexts {
				ValidateContext(ctx)
			}
		})
		Step("teardown all apps", func() {
			for _, ctx := range contexts {
				TearDownContext(ctx, nil)
			}
		})

	})
})

var _ = AfterSuite(func() {
	PerformSystemCheck()
	CollectSupport()
	ValidateCleanup()
})

func init() {
	ParseFlags()
}
<commit_msg>give some time to app to scale up and down to proper update specs (#210)<commit_after>package tests

import (
	"fmt"
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/portworx/torpedo/drivers/node"
	"github.com/portworx/torpedo/drivers/scheduler"
	. "github.com/portworx/torpedo/tests"
)

func TestBasic(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Torpedo : Basic")
}

var _ = BeforeSuite(func() {
	InitInstance()
})

// This test performs basic test of starting an application and destroying it (along with storage)
var _ = Describe("{SetupTeardown}", func() {
	It("has to setup, validate and teardown apps", func() {
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("setupteardown-%d", i))...)
		}

		opts := make(map[string]bool)
		opts[scheduler.OptionsWaitForResourceLeakCleanup] = true

		for _, ctx := range contexts {
			TearDownContext(ctx, opts)
		}
	})
})

// Volume Driver Plugin is down, unavailable - and the client container should not be impacted.
var _ = Describe("{VolumeDriverDown}", func() {
	It("has to schedule apps and stop volume driver on app nodes", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldriverdown-%d", i))...)
		}

		Step("get nodes for all apps in test and bounce volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node
				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					appNodes, err = Inst().S.GetNodesForApp(ctx)
					Expect(err).NotTo(HaveOccurred())
					Expect(appNodes).NotTo(BeEmpty())
				})

				Step(
					fmt.Sprintf("stop volume driver %s on app %s's nodes: %v",
						Inst().V.String(), ctx.App.Key, appNodes),
					func() {
						StopVolDriverAndWait(appNodes)
					})

				Step("starting volume driver", func() {
					StartVolDriverAndWait(appNodes)
				})

				Step("Giving few seconds for volume driver to stabilize", func() {
					time.Sleep(20 * time.Second)
				})
			}
		})

		Step("destroy apps", func() {
			opts := make(map[string]bool)
			opts[scheduler.OptionsWaitForResourceLeakCleanup] = true
			for _, ctx := range contexts {
				TearDownContext(ctx, opts)
			}
		})

	})
})

// Volume Driver Plugin is down, unavailable on the nodes where the volumes are
// attached - and the client container should not be impacted.
var _ = Describe("{VolumeDriverDownAttachedNode}", func() {
	It("has to schedule apps and stop volume driver on nodes where volumes are attached", func() {
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldriverdownattachednode-%d", i))...)
		}

		Step("get nodes for all apps in test and restart volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node

				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					volumes, err := Inst().S.GetVolumes(ctx)
					Expect(err).NotTo(HaveOccurred())

					nodeMap := make(map[string]struct{})
					for _, v := range volumes {
						n, err := Inst().V.GetNodeForVolume(v)
						Expect(err).NotTo(HaveOccurred())

						if n == nil {
							continue
						}

						if _, exists := nodeMap[n.Name]; !exists {
							nodeMap[n.Name] = struct{}{}
							appNodes = append(appNodes, *n)
						}
					}
				})

				Step(fmt.Sprintf("stop volume driver %s on app %s's nodes: %v",
					Inst().V.String(), ctx.App.Key, appNodes), func() {
					StopVolDriverAndWait(appNodes)
				})

				Step("starting volume driver", func() {
					StartVolDriverAndWait(appNodes)
				})

				Step("Giving few seconds for volume driver to stabilize", func() {
					time.Sleep(20 * time.Second)
				})
			}
		})

		opts := make(map[string]bool)
		opts[scheduler.OptionsWaitForResourceLeakCleanup] = true
		ValidateAndDestroy(contexts, opts)
	})
})

// Volume Driver Plugin has crashed - and the client container should not be impacted.
var _ = Describe("{VolumeDriverCrash}", func() {
	It("has to schedule apps and crash volume driver on app nodes", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldrivercrash-%d", i))...)
		}

		Step("get nodes for all apps in test and crash volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node
				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					appNodes, err = Inst().S.GetNodesForApp(ctx)
					Expect(err).NotTo(HaveOccurred())
					Expect(appNodes).NotTo(BeEmpty())
				})

				Step(
					fmt.Sprintf("crash volume driver %s on app %s's nodes: %v",
						Inst().V.String(), ctx.App.Key, appNodes),
					func() {
						CrashVolDriverAndWait(appNodes)
					})
			}
		})

		opts := make(map[string]bool)
		opts[scheduler.OptionsWaitForResourceLeakCleanup] = true
		ValidateAndDestroy(contexts, opts)
	})
})

// Volume driver plugin is down and the client container gets terminated.
// There is a lost unmount call in this case. When the volume driver is
// back up, we should be able to detach and delete the volume.
var _ = Describe("{VolumeDriverAppDown}", func() {
	It("has to schedule apps, stop volume driver on app nodes and destroy apps", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("voldriverappdown-%d", i))...)
		}

		Step("get nodes for all apps in test and bounce volume driver", func() {
			for _, ctx := range contexts {
				var appNodes []node.Node
				Step(fmt.Sprintf("get nodes for %s app", ctx.App.Key), func() {
					appNodes, err = Inst().S.GetNodesForApp(ctx)
					Expect(err).NotTo(HaveOccurred())
					Expect(appNodes).NotTo(BeEmpty())
				})

				Step(fmt.Sprintf("stop volume driver %s on app %s's nodes: %v",
					Inst().V.String(), ctx.App.Key, appNodes), func() {
					StopVolDriverAndWait(appNodes)
				})

				Step(fmt.Sprintf("destroy app: %s", ctx.App.Key), func() {
					err = Inst().S.Destroy(ctx, nil)
					Expect(err).NotTo(HaveOccurred())

					Step("wait for few seconds for app destroy to trigger", func() {
						time.Sleep(10 * time.Second)
					})
				})

				Step("restarting volume driver", func() {
					StartVolDriverAndWait(appNodes)
				})

				Step(fmt.Sprintf("wait for destroy of app: %s", ctx.App.Key), func() {
					err = Inst().S.WaitForDestroy(ctx)
					Expect(err).NotTo(HaveOccurred())
				})

				DeleteVolumesAndWait(ctx)
			}
		})
	})
})

// This test deletes all tasks of an application and checks if app converges back to desired state
var _ = Describe("{AppTasksDown}", func() {
	It("has to schedule app and delete app tasks", func() {
		var err error
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("apptasksdown-%d", i))...)
		}

		Step("delete all application tasks", func() {
			for _, ctx := range contexts {
				Step(fmt.Sprintf("delete tasks for app: %s", ctx.App.Key), func() {
					err = Inst().S.DeleteTasks(ctx)
					Expect(err).NotTo(HaveOccurred())
				})

				ValidateContext(ctx)
			}
		})

		Step("teardown all apps", func() {
			for _, ctx := range contexts {
				TearDownContext(ctx, nil)
			}
		})
	})
})

// This test scales up and down an application and checks if app has actually scaled accordingly
var _ = Describe("{AppScaleUpAndDown}", func() {
	It("has to scale up and scale down the app", func() {
		var contexts []*scheduler.Context
		for i := 0; i < Inst().ScaleFactor; i++ {
			contexts = append(contexts, ScheduleAndValidate(fmt.Sprintf("applicationscaleupdown-%d", i))...)
		}

		Step("Scale up and down all app", func() {
			for _, ctx := range contexts {
				Step(fmt.Sprintf("scale up app: %s by %d ", ctx.App.Key, len(node.GetWorkerNodes())), func() {
					applicationScaleUpMap, err := Inst().S.GetScaleFactorMap(ctx)
					Expect(err).NotTo(HaveOccurred())
					for name, scale := range applicationScaleUpMap {
						applicationScaleUpMap[name] = scale + int32(len(node.GetWorkerNodes()))
					}
					err = Inst().S.ScaleApplication(ctx, applicationScaleUpMap)
					Expect(err).NotTo(HaveOccurred())
				})

				Step("Giving few seconds for scaled up applications to stabilize", func() {
					time.Sleep(10 * time.Second)
				})

				ValidateContext(ctx)

				Step(fmt.Sprintf("scale down app %s by 1", ctx.App.Key), func() {
					applicationScaleDownMap, err := Inst().S.GetScaleFactorMap(ctx)
					Expect(err).NotTo(HaveOccurred())
					for name, scale := range applicationScaleDownMap {
						applicationScaleDownMap[name] = scale - 1
					}
					err = Inst().S.ScaleApplication(ctx, applicationScaleDownMap)
					Expect(err).NotTo(HaveOccurred())
				})

				Step("Giving few seconds for scaled down applications to stabilize", func() {
					time.Sleep(10 * time.Second)
				})

				ValidateContext(ctx)
			}
		})

		Step("teardown all apps", func() {
			for _, ctx := range contexts {
				TearDownContext(ctx, nil)
			}
		})

	})
})

var _ = AfterSuite(func() {
	PerformSystemCheck()
	CollectSupport()
	ValidateCleanup()
})

func init() {
	ParseFlags()
}
<|endoftext|>