<commit_before>package sched

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
)

func (s *Schedule) Status(ak AlertKey) *State {
	s.Lock()
	st := s.status[ak]
	s.Unlock()
	return st
}

func (s *Schedule) Check() {
	s.runStates = make(map[AlertKey]Status)
	s.cache = opentsdb.NewCache(s.Conf.TsdbHost, s.Conf.ResponseLimit)
	for _, a := range s.Conf.Alerts {
		s.CheckAlert(a)
	}
	s.CheckUnknown()
	checkNotify := false
	silenced := s.Silenced()
	for ak, status := range s.runStates {
		state := s.Status(ak)
		last := state.Append(status)
		a := s.Conf.Alerts[ak.Name()]
		if status > stNormal {
			var subject = new(bytes.Buffer)
			if err := s.ExecuteSubject(subject, a, state); err != nil {
				log.Println(err)
			}
			state.Subject = subject.String()
			state.Open = true
		}
		// On state increase, clear old notifications and notify current.
		// On state decrease, and if the old alert was already acknowledged, notify current.
		// If the old alert was not acknowledged, do nothing.
		// Do nothing if state did not change.
		notify := func(notifications map[string]*conf.Notification) {
			for _, n := range notifications {
				s.Notify(state, n)
				checkNotify = true
			}
		}
		notifyCurrent := func() {
			state.NeedAck = true
			if _, present := silenced[ak]; present {
				return
			}
			switch status {
			case stCritical, stUnknown:
				notify(a.CritNotification)
			case stWarning:
				notify(a.WarnNotification)
			}
		}
		clearOld := func() {
			state.NeedAck = false
			s.Lock()
			delete(s.Notifications, ak)
			s.Unlock()
		}
		if status > last {
			clearOld()
			notifyCurrent()
		} else if status < last {
			if _, hasOld := s.Notifications[ak]; hasOld {
				notifyCurrent()
			}
		}
	}
	if checkNotify {
		s.nc <- true
	}
	s.Save()
}

func (s *Schedule) CheckUnknown() {
	s.Lock()
	for ak, st := range s.status {
		if st.Forgotten {
			continue
		}
		t := s.Conf.Alerts[ak.Name()].Unknown
		if t == 0 {
			t = s.Conf.Unknown
		}
		if t == 0 {
			continue
		}
		if time.Since(st.Touched) < t {
			continue
		}
		s.runStates[ak] = stUnknown
	}
	s.Unlock()
}

func (s *Schedule) CheckAlert(a *conf.Alert) {
	log.Printf("checking alert %v", a.Name)
	start := time.Now()
	crits, err := s.CheckExpr(a, a.Crit, stCritical, nil)
	var warns AlertKeys
	if err == nil {
		warns, _ = s.CheckExpr(a, a.Warn, stWarning, crits)
	}
	log.Printf("done checking alert %v (%s): %v crits, %v warns", a.Name, time.Since(start), len(crits), len(warns))
}

func (s *Schedule) CheckExpr(a *conf.Alert, e *expr.Expr, checkStatus Status, ignore AlertKeys) (alerts AlertKeys, err error) {
	if e == nil {
		return
	}
	defer func() {
		if err == nil {
			return
		}
		collect.Add("check.errs", opentsdb.TagSet{"metric": a.Name}, 1)
		log.Println(err)
	}()
	results, _, err := e.Execute(s.cache, nil)
	if err != nil {
		return
	}
Loop:
	for _, r := range results {
		if a.Squelched(r.Group) {
			continue
		}
		ak := NewAlertKey(a.Name, r.Group)
		for _, v := range ignore {
			if ak == v {
				continue Loop
			}
		}
		state := s.Status(ak)
		if state == nil {
			state = &State{
				Alert: ak.Name(),
				Tags:  r.Group.Tags(),
				Group: r.Group,
			}
			s.Lock()
			s.status[ak] = state
			s.Unlock()
		}
		state.Touch()
		status := checkStatus
		state.Computations = r.Computations
		var n float64
		switch v := r.Value.(type) {
		case expr.Number:
			n = float64(v)
		case expr.Scalar:
			n = float64(v)
		default:
			err = fmt.Errorf("expected number or scalar")
			return
		}
		if n != 0 {
			state.Expr = e.String()
			alerts = append(alerts, ak)
		} else {
			status = stNormal
		}
		if s.runStates != nil && status > s.runStates[ak] {
			s.runStates[ak] = status
		}
	}
	return
}
<commit_msg>Don't execute the alert template if we're unknown<commit_after>package sched

import (
	"bytes"
	"fmt"
	"log"
	"time"

	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/tsaf/_third_party/github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/tsaf/conf"
	"github.com/StackExchange/tsaf/expr"
)

func (s *Schedule) Status(ak AlertKey) *State {
	s.Lock()
	st := s.status[ak]
	s.Unlock()
	return st
}

func (s *Schedule) Check() {
	s.runStates = make(map[AlertKey]Status)
	s.cache = opentsdb.NewCache(s.Conf.TsdbHost, s.Conf.ResponseLimit)
	for _, a := range s.Conf.Alerts {
		s.CheckAlert(a)
	}
	s.CheckUnknown()
	checkNotify := false
	silenced := s.Silenced()
	for ak, status := range s.runStates {
		state := s.Status(ak)
		last := state.Append(status)
		a := s.Conf.Alerts[ak.Name()]
		if status > stNormal {
			var subject = new(bytes.Buffer)
			if status == stUnknown {
				fmt.Fprint(subject, "unknown")
			} else {
				if err := s.ExecuteSubject(subject, a, state); err != nil {
					log.Println(err)
				}
			}
			state.Subject = subject.String()
			state.Open = true
		}
		// On state increase, clear old notifications and notify current.
		// On state decrease, and if the old alert was already acknowledged, notify current.
		// If the old alert was not acknowledged, do nothing.
		// Do nothing if state did not change.
		notify := func(notifications map[string]*conf.Notification) {
			for _, n := range notifications {
				s.Notify(state, n)
				checkNotify = true
			}
		}
		notifyCurrent := func() {
			state.NeedAck = true
			if _, present := silenced[ak]; present {
				return
			}
			switch status {
			case stCritical, stUnknown:
				notify(a.CritNotification)
			case stWarning:
				notify(a.WarnNotification)
			}
		}
		clearOld := func() {
			state.NeedAck = false
			s.Lock()
			delete(s.Notifications, ak)
			s.Unlock()
		}
		if status > last {
			clearOld()
			notifyCurrent()
		} else if status < last {
			if _, hasOld := s.Notifications[ak]; hasOld {
				notifyCurrent()
			}
		}
	}
	if checkNotify {
		s.nc <- true
	}
	s.Save()
}

func (s *Schedule) CheckUnknown() {
	s.Lock()
	for ak, st := range s.status {
		if st.Forgotten {
			continue
		}
		t := s.Conf.Alerts[ak.Name()].Unknown
		if t == 0 {
			t = s.Conf.Unknown
		}
		if t == 0 {
			continue
		}
		if time.Since(st.Touched) < t {
			continue
		}
		s.runStates[ak] = stUnknown
	}
	s.Unlock()
}

func (s *Schedule) CheckAlert(a *conf.Alert) {
	log.Printf("checking alert %v", a.Name)
	start := time.Now()
	crits, err := s.CheckExpr(a, a.Crit, stCritical, nil)
	var warns AlertKeys
	if err == nil {
		warns, _ = s.CheckExpr(a, a.Warn, stWarning, crits)
	}
	log.Printf("done checking alert %v (%s): %v crits, %v warns", a.Name, time.Since(start), len(crits), len(warns))
}

func (s *Schedule) CheckExpr(a *conf.Alert, e *expr.Expr, checkStatus Status, ignore AlertKeys) (alerts AlertKeys, err error) {
	if e == nil {
		return
	}
	defer func() {
		if err == nil {
			return
		}
		collect.Add("check.errs", opentsdb.TagSet{"metric": a.Name}, 1)
		log.Println(err)
	}()
	results, _, err := e.Execute(s.cache, nil)
	if err != nil {
		return
	}
Loop:
	for _, r := range results {
		if a.Squelched(r.Group) {
			continue
		}
		ak := NewAlertKey(a.Name, r.Group)
		for _, v := range ignore {
			if ak == v {
				continue Loop
			}
		}
		state := s.Status(ak)
		if state == nil {
			state = &State{
				Alert: ak.Name(),
				Tags:  r.Group.Tags(),
				Group: r.Group,
			}
			s.Lock()
			s.status[ak] = state
			s.Unlock()
		}
		state.Touch()
		status := checkStatus
		state.Computations = r.Computations
		var n float64
		switch v := r.Value.(type) {
		case expr.Number:
			n = float64(v)
		case expr.Scalar:
			n = float64(v)
		default:
			err = fmt.Errorf("expected number or scalar")
			return
		}
		if n != 0 {
			state.Expr = e.String()
			alerts = append(alerts, ak)
		} else {
			status = stNormal
		}
		if s.runStates != nil && status > s.runStates[ak] {
			s.runStates[ak] = status
		}
	}
	return
}
<|endoftext|>


<commit_before>package scraper

import (
	"net/url"
	"path/filepath"
	"strings"
)

// RemoveAnchor removes anchors from URLS
func (s *Scraper) RemoveAnchor(path string) string {
	sl := strings.LastIndexByte(path, '/')
	if sl == -1 {
		return path
	}
	an := strings.LastIndexByte(path[sl+1:], '#')
	if an == -1 {
		return path
	}
	return path[:sl+an+1]
}

func (s *Scraper) resolveURL(base *url.URL, reference string, linkIsAPage bool, relativeToRoot string) string {
	ur, err := url.Parse(reference)
	if err != nil {
		return ""
	}

	var resolvedurl *url.URL
	if ur.Host != "" && ur.Host != s.URL.Host {
		if linkIsAPage { // do not change links to external websites
			return ""
		}

		resolvedurl = base.ResolveReference(ur)
		resolvedurl.Path = filepath.Join("_"+ur.Host, resolvedurl.Path)
	} else {
		if linkIsAPage {
			resolvedurl = base.ResolveReference(GetPageURL(ur))
		} else {
			resolvedurl = base.ResolveReference(ur)
		}
	}

	if resolvedurl.Host == s.URL.Host {
		if strings.Contains(resolvedurl.Path, s.URL.Path) {
			resolvedurl.Path = strings.Replace(resolvedurl.Path, s.URL.Path, "", 1)
			relativeToRoot = ""
		}
	}

	resolvedurl.Host = ""   // remove host
	resolvedurl.Scheme = "" // remove http/https
	resolved := resolvedurl.String()

	if resolved == "" {
		resolved = "/" // website root
	} else {
		if resolved[0] == '/' && len(relativeToRoot) > 0 {
			resolved = relativeToRoot + resolved[1:]
		} else {
			resolved = relativeToRoot + resolved
		}
	}

	if linkIsAPage {
		if resolved[len(resolved)-1] == '/' {
			resolved += PageDirIndex // link dir index to index.html
		} else {
			l := strings.LastIndexByte(resolved, '/')
			if l != -1 && l < len(resolved) && resolved[l+1] == '#' {
				resolved = resolved[:l+1] + PageDirIndex + resolved[l+1:] // link anchor correct
			}
		}
	}

	resolved = strings.TrimPrefix(resolved, "/")
	return resolved
}

func (s *Scraper) urlRelativeToRoot(URL *url.URL) string {
	var rel string
	splits := strings.Split(URL.Path, "/")
	for i := range splits {
		if len(splits[i]) > 0 {
			rel += "../"
		}
	}
	return rel
}
<commit_msg>Fix relative path evaluation for links<commit_after>package scraper

import (
	"net/url"
	"path/filepath"
	"strings"
)

// RemoveAnchor removes anchors from URLS
func (s *Scraper) RemoveAnchor(path string) string {
	sl := strings.LastIndexByte(path, '/')
	if sl == -1 {
		return path
	}
	an := strings.LastIndexByte(path[sl+1:], '#')
	if an == -1 {
		return path
	}
	return path[:sl+an+1]
}

func (s *Scraper) resolveURL(base *url.URL, reference string, linkIsAPage bool, relativeToRoot string) string {
	ur, err := url.Parse(reference)
	if err != nil {
		return ""
	}

	var resolvedurl *url.URL
	if ur.Host != "" && ur.Host != s.URL.Host {
		if linkIsAPage { // do not change links to external websites
			return ""
		}

		resolvedurl = base.ResolveReference(ur)
		resolvedurl.Path = filepath.Join("_"+ur.Host, resolvedurl.Path)
	} else {
		if linkIsAPage {
			resolvedurl = base.ResolveReference(GetPageURL(ur))
		} else {
			resolvedurl = base.ResolveReference(ur)
		}
	}

	if resolvedurl.Host == s.URL.Host {
		if strings.Contains(resolvedurl.Path, s.URL.Path) {
			resolvedurl.Path = strings.Replace(resolvedurl.Path, s.URL.Path, "", 1)
			relativeToRoot = ""
		}
	}

	resolvedurl.Host = ""   // remove host
	resolvedurl.Scheme = "" // remove http/https
	resolved := resolvedurl.String()

	if resolved == "" {
		resolved = "/" // website root
	} else {
		if resolved[0] == '/' && len(relativeToRoot) > 0 {
			resolved = relativeToRoot + resolved[1:]
		} else {
			resolved = relativeToRoot + resolved
		}
	}

	if linkIsAPage {
		if resolved[len(resolved)-1] == '/' {
			resolved += PageDirIndex // link dir index to index.html
		} else {
			l := strings.LastIndexByte(resolved, '/')
			if l != -1 && l < len(resolved) && resolved[l+1] == '#' {
				resolved = resolved[:l+1] + PageDirIndex + resolved[l+1:] // link anchor correct
			}
		}
	}

	resolved = strings.TrimPrefix(resolved, "/")
	return resolved
}

func (s *Scraper) urlRelativeToRoot(URL *url.URL) string {
	var rel string
	splits := strings.Split(URL.Path, "/")
	for i := range splits {
		if (len(splits[i]) > 0) && (i < len(splits)-1) {
			rel += "../"
		}
	}
	return rel
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 The psh Authors. All rights reserved.

package psh

import (
	"bytes"
	"os/exec"
	"strings"
)

// git rev-parse --show-toplevel : shows root of the repo
// git status --porcelain -b : shows conmplete status of the repo

// SegmentGit implements the git SCM partial of the prompt.
//
// It renders the current branch if the folder contains a repository.
type SegmentGit struct {
	Data []byte
}

// NewSegmentGit creates an instace of SegmentHostname type.
func NewSegmentGit() *SegmentGit {
	segment := &SegmentGit{}
	return segment
}

// Compile ...
func (s *SegmentGit) Compile() {
	currentBranch := getCurrentBranch()
	s.Data = []byte(currentBranch)
}

// Render renders the segment results.
func (s *SegmentGit) Render() []byte {
	var b bytes.Buffer
	if len(s.Data) != 0 {
		b.Write(SetBackground(202))
		b.Write([]byte(" "))
		b.Write(s.Data)
		b.Write([]byte(" "))
	}
	return b.Bytes()
}

func getCurrentBranch() string {
	command := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
	commandOut, err := command.Output()
	if err != nil {
		return ""
	}
	currentBranch := strings.Trim(string(commandOut), "\n")
	return currentBranch
}
<commit_msg>better colors<commit_after>// Copyright 2016-2017 The psh Authors. All rights reserved.

package psh

import (
	"bytes"
	"os/exec"
	"strings"
)

// git rev-parse --show-toplevel : shows root of the repo
// git status --porcelain -b : shows conmplete status of the repo

// SegmentGit implements the git SCM partial of the prompt.
//
// It renders the current branch if the folder contains a repository.
type SegmentGit struct {
	Data []byte
}

// NewSegmentGit creates an instace of SegmentHostname type.
func NewSegmentGit() *SegmentGit {
	segment := &SegmentGit{}
	return segment
}

// Compile ...
func (s *SegmentGit) Compile() {
	currentBranch := getCurrentBranch()
	s.Data = []byte(currentBranch)
}

// Render renders the segment results.
func (s *SegmentGit) Render() []byte {
	var b bytes.Buffer
	if len(s.Data) != 0 {
		b.Write(SetBackground(148))
		b.Write([]byte(" "))
		b.Write(SetForeground(0))
		b.Write(s.Data)
		b.Write(ResetForeground())
		b.Write([]byte(" "))
	}
	return b.Bytes()
}

func getCurrentBranch() string {
	command := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD")
	commandOut, err := command.Output()
	if err != nil {
		return ""
	}
	currentBranch := strings.Trim(string(commandOut), "\n")
	return currentBranch
}
<|endoftext|>


<commit_before>package operators

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	. "github.com/onsi/ginkgo"

	exutil "github.com/openshift/origin/test/extended/util"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
)

var _ = Describe("[sig-arch] Managed cluster", func() {
	oc := exutil.NewCLIWithoutNamespace("operators")
	It("should ensure pods use downstream images from our release image with proper ImagePullPolicy", func() {
		if len(os.Getenv("TEST_UNSUPPORTED_ALLOW_VERSION_SKEW")) > 0 {
			e2eskipper.Skipf("Test is disabled to allow cluster components to have different versions")
		}
		imagePullSecret, err := oc.KubeFramework().ClientSet.CoreV1().Secrets("openshift-config").Get(context.Background(), "pull-secret", metav1.GetOptions{})
		if err != nil {
			e2e.Failf("unable to get pull secret for cluster: %v", err)
		}

		// cache file to local temp location
		imagePullFile, err := ioutil.TempFile("", "image-pull-secret")
		if err != nil {
			e2e.Failf("unable to create a temporary file: %v", err)
		}
		defer os.Remove(imagePullFile.Name())

		// write the content
		imagePullSecretBytes := imagePullSecret.Data[".dockerconfigjson"]
		if _, err := imagePullFile.Write(imagePullSecretBytes); err != nil {
			e2e.Failf("unable to write pull secret to temp file: %v", err)
		}
		if err := imagePullFile.Close(); err != nil {
			e2e.Failf("unable to close file: %v", err)
		}

		// find out the current installed release info using the temp file
		out, err := oc.Run("adm", "release", "info").Args("--pullspecs", "-o", "json", "--registry-config", imagePullFile.Name()).Output()
		if err != nil {
			// TODO need to determine why release tests are not having access to read payload
			e2e.Logf("unable to read release payload with error: %v", err)
			return
		}
		releaseInfo := &ReleaseInfo{}
		if err := json.Unmarshal([]byte(out), &releaseInfo); err != nil {
			e2e.Failf("unable to decode release payload with error: %v", err)
		}
		e2e.Logf("Release Info image=%s", releaseInfo.Image)
		e2e.Logf("Release Info number of tags %v", len(releaseInfo.References.Spec.Tags))

		// valid images include the release image and all its tagged references
		validImages := sets.NewString()
		validImages.Insert(releaseInfo.Image)
		if releaseInfo.References == nil {
			e2e.Failf("no references found")
		}
		for i := range releaseInfo.References.Spec.Tags {
			tag := releaseInfo.References.Spec.Tags[i]
			if tag.From != nil && tag.From.Kind == "DockerImage" {
				validImages.Insert(tag.From.Name)
			}
		}

		// iterate over the references to find valid images
		pods, err := oc.KubeFramework().ClientSet.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{})
		if err != nil {
			e2e.Failf("unable to list pods: %v", err)
		}

		// skip containers that are known to be already succeeded when this test run and they always result
		// into error: cannot exec into a container in a completed pod; current phase is Succeeded.
		skipPodContainersNames := sets.NewString(
			"installer",
			"pruner",
		)

		// list of pods that use images not in the release payload
		invalidPodContainerImages := sets.NewString()
		invalidPodContainerImagePullPolicy := sets.NewString()
		invalidPodContainerDownstreamImages := sets.NewString()
		// a pod in a namespace that begins with kube-* or openshift-* must come from our release payload
		// TODO components in openshift-operators may not come from our payload, may want to weaken restriction
		namespacePrefixes := sets.NewString("kube-", "openshift-")
		for i := range pods.Items {
			pod := pods.Items[i]
			for _, prefix := range namespacePrefixes.List() {
				if !strings.HasPrefix(pod.Namespace, prefix) {
					continue
				}
				containersToInspect := []v1.Container{}
				for j := range pod.Spec.InitContainers {
					if skipPodContainersNames.Has(pod.Spec.InitContainers[j].Name) {
						continue
					}
					containersToInspect = append(containersToInspect, pod.Spec.InitContainers[j])
				}
				for j := range pod.Spec.Containers {
					if skipPodContainersNames.Has(pod.Spec.Containers[j].Name) {
						continue
					}
					containersToInspect = append(containersToInspect, pod.Spec.Containers[j])
				}
				for j := range containersToInspect {
					container := containersToInspect[j]
					if !validImages.Has(container.Image) {
						invalidPodContainerImages.Insert(fmt.Sprintf("%s/%s/%s image=%s", pod.Namespace, pod.Name, container.Name, container.Image))
					}

					if container.ImagePullPolicy != v1.PullIfNotPresent {
						invalidPodContainerImagePullPolicy.Insert(fmt.Sprintf("%s/%s/%s imagePullPolicy=%s", pod.Namespace, pod.Name, container.Name, container.ImagePullPolicy))
					}
				}
				// check if the container's image from the downstream.
				for j := range pod.Spec.Containers {
					containerName := pod.Spec.Containers[j].Name
					commands := []string{
						"exec",
						pod.Name,
						"-c",
						containerName,
						"--",
						"cat",
						"/etc/redhat-release",
					}
					oc.SetNamespace(pod.Namespace)
					result, err := oc.AsAdmin().Run(commands...).Args().Output()
					if err != nil {
						e2e.Logf("unable to run command:%v with error: %v", commands, err)
						continue
					}
					e2e.Logf("Image relase info:%s", result)
					if !strings.Contains(result, "Red Hat Enterprise Linux Server") {
						invalidPodContainerDownstreamImages.Insert(fmt.Sprintf("%s/%s invalid downstream image!", pod.Name, containerName))
					}
				}
			}
		}
		// log for debugging output before we ultimately fail
		e2e.Logf("Pods found with invalid container images not present in release payload: %s", strings.Join(invalidPodContainerImages.List(), "\n"))
		e2e.Logf("Pods found with invalid container image pull policy not equal to IfNotPresent: %s", strings.Join(invalidPodContainerImagePullPolicy.List(), "\n"))
		e2e.Logf("Pods with invalid dowstream images: %s", strings.Join(invalidPodContainerDownstreamImages.List(), "\n"))
		if len(invalidPodContainerImages) > 0 {
			e2e.Failf("Pods found with invalid container images not present in release payload: %s", strings.Join(invalidPodContainerImages.List(), "\n"))
		}
		if len(invalidPodContainerImagePullPolicy) > 0 {
			e2e.Failf("Pods found with invalid container image pull policy not equal to IfNotPresent: %s", strings.Join(invalidPodContainerImagePullPolicy.List(), "\n"))
		}
		if len(invalidPodContainerDownstreamImages) > 0 {
			e2e.Failf("Pods with invalid dowstream images: %s", strings.Join(invalidPodContainerDownstreamImages.List(), "\n"))
		}
	})
})
<commit_msg>test: Broaden check for base images that are RHEL8<commit_after>package operators

import (
	"context"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"

	. "github.com/onsi/ginkgo"

	exutil "github.com/openshift/origin/test/extended/util"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	e2e "k8s.io/kubernetes/test/e2e/framework"
	e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
)

var _ = Describe("[sig-arch] Managed cluster", func() {
	oc := exutil.NewCLIWithoutNamespace("operators")
	It("should ensure pods use downstream images from our release image with proper ImagePullPolicy", func() {
		if len(os.Getenv("TEST_UNSUPPORTED_ALLOW_VERSION_SKEW")) > 0 {
			e2eskipper.Skipf("Test is disabled to allow cluster components to have different versions")
		}
		imagePullSecret, err := oc.KubeFramework().ClientSet.CoreV1().Secrets("openshift-config").Get(context.Background(), "pull-secret", metav1.GetOptions{})
		if err != nil {
			e2e.Failf("unable to get pull secret for cluster: %v", err)
		}

		// cache file to local temp location
		imagePullFile, err := ioutil.TempFile("", "image-pull-secret")
		if err != nil {
			e2e.Failf("unable to create a temporary file: %v", err)
		}
		defer os.Remove(imagePullFile.Name())

		// write the content
		imagePullSecretBytes := imagePullSecret.Data[".dockerconfigjson"]
		if _, err := imagePullFile.Write(imagePullSecretBytes); err != nil {
			e2e.Failf("unable to write pull secret to temp file: %v", err)
		}
		if err := imagePullFile.Close(); err != nil {
			e2e.Failf("unable to close file: %v", err)
		}

		// find out the current installed release info using the temp file
		out, err := oc.Run("adm", "release", "info").Args("--pullspecs", "-o", "json", "--registry-config", imagePullFile.Name()).Output()
		if err != nil {
			// TODO need to determine why release tests are not having access to read payload
			e2e.Logf("unable to read release payload with error: %v", err)
			return
		}
		releaseInfo := &ReleaseInfo{}
		if err := json.Unmarshal([]byte(out), &releaseInfo); err != nil {
			e2e.Failf("unable to decode release payload with error: %v", err)
		}
		e2e.Logf("Release Info image=%s", releaseInfo.Image)
		e2e.Logf("Release Info number of tags %v", len(releaseInfo.References.Spec.Tags))

		// valid images include the release image and all its tagged references
		validImages := sets.NewString()
		validImages.Insert(releaseInfo.Image)
		if releaseInfo.References == nil {
			e2e.Failf("no references found")
		}
		for i := range releaseInfo.References.Spec.Tags {
			tag := releaseInfo.References.Spec.Tags[i]
			if tag.From != nil && tag.From.Kind == "DockerImage" {
				validImages.Insert(tag.From.Name)
			}
		}

		// iterate over the references to find valid images
		pods, err := oc.KubeFramework().ClientSet.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{})
		if err != nil {
			e2e.Failf("unable to list pods: %v", err)
		}

		// skip containers that are known to be already succeeded when this test run and they always result
		// into error: cannot exec into a container in a completed pod; current phase is Succeeded.
		skipPodContainersNames := sets.NewString(
			"installer",
			"pruner",
		)

		// list of pods that use images not in the release payload
		invalidPodContainerImages := sets.NewString()
		invalidPodContainerImagePullPolicy := sets.NewString()
		invalidPodContainerDownstreamImages := sets.NewString()
		// a pod in a namespace that begins with kube-* or openshift-* must come from our release payload
		// TODO components in openshift-operators may not come from our payload, may want to weaken restriction
		namespacePrefixes := sets.NewString("kube-", "openshift-")
		for i := range pods.Items {
			pod := pods.Items[i]
			for _, prefix := range namespacePrefixes.List() {
				if !strings.HasPrefix(pod.Namespace, prefix) {
					continue
				}
				containersToInspect := []v1.Container{}
				for j := range pod.Spec.InitContainers {
					if skipPodContainersNames.Has(pod.Spec.InitContainers[j].Name) {
						continue
					}
					containersToInspect = append(containersToInspect, pod.Spec.InitContainers[j])
				}
				for j := range pod.Spec.Containers {
					if skipPodContainersNames.Has(pod.Spec.Containers[j].Name) {
						continue
					}
					containersToInspect = append(containersToInspect, pod.Spec.Containers[j])
				}
				for j := range containersToInspect {
					container := containersToInspect[j]
					if !validImages.Has(container.Image) {
						invalidPodContainerImages.Insert(fmt.Sprintf("%s/%s/%s image=%s", pod.Namespace, pod.Name, container.Name, container.Image))
					}

					if container.ImagePullPolicy != v1.PullIfNotPresent {
						invalidPodContainerImagePullPolicy.Insert(fmt.Sprintf("%s/%s/%s imagePullPolicy=%s", pod.Namespace, pod.Name, container.Name, container.ImagePullPolicy))
					}
				}
				// check if the container's image from the downstream.
				for j := range pod.Spec.Containers {
					containerName := pod.Spec.Containers[j].Name
					commands := []string{
						"exec",
						pod.Name,
						"-c",
						containerName,
						"--",
						"cat",
						"/etc/redhat-release",
					}
					oc.SetNamespace(pod.Namespace)
					result, err := oc.AsAdmin().Run(commands...).Args().Output()
					if err != nil {
						e2e.Logf("unable to run command:%v with error: %v", commands, err)
						continue
					}
					e2e.Logf("Image release info: %s", result)
					if !strings.Contains(result, "Red Hat Enterprise Linux") {
						invalidPodContainerDownstreamImages.Insert(fmt.Sprintf("%s/%s invalid downstream image!", pod.Name, containerName))
					}
				}
			}
		}
		// log for debugging output before we ultimately fail
		e2e.Logf("Pods found with invalid container images not present in release payload: %s", strings.Join(invalidPodContainerImages.List(), "\n"))
		e2e.Logf("Pods found with invalid container image pull policy not equal to IfNotPresent: %s", strings.Join(invalidPodContainerImagePullPolicy.List(), "\n"))
		e2e.Logf("Pods with invalid dowstream images: %s", strings.Join(invalidPodContainerDownstreamImages.List(), "\n"))
		if len(invalidPodContainerImages) > 0 {
			e2e.Failf("Pods found with invalid container images not present in release payload: %s", strings.Join(invalidPodContainerImages.List(), "\n"))
		}
		if len(invalidPodContainerImagePullPolicy) > 0 {
			e2e.Failf("Pods found with invalid container image pull policy not equal to IfNotPresent: %s", strings.Join(invalidPodContainerImagePullPolicy.List(), "\n"))
		}
		if len(invalidPodContainerDownstreamImages) > 0 {
			e2e.Failf("Pods with invalid dowstream images: %s", strings.Join(invalidPodContainerDownstreamImages.List(), "\n"))
		}
	})
})
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"bufio"
	"net"
	"time"

	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/kvproto/pkg/msgpb"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"github.com/pingcap/kvproto/pkg/util"
	"github.com/pingcap/pd/pkg/metrics"
)

const (
	readBufferSize  = 8 * 1024
	writeBufferSize = 8 * 1024
)

type conn struct {
	s *Server

	rb         *bufio.Reader
	wb         *bufio.Writer
	conn       net.Conn
	leaderConn net.Conn
}

func newConn(s *Server, netConn net.Conn, bufrw *bufio.ReadWriter) (*conn, error) {
	s.connsLock.Lock()
	defer s.connsLock.Unlock()

	c := &conn{
		s:    s,
		rb:   bufrw.Reader,
		wb:   bufrw.Writer,
		conn: netConn,
	}

	s.conns[c] = struct{}{}

	return c, nil
}

func (c *conn) run() {
	defer func() {
		c.s.wg.Done()
		c.close()

		c.s.connsLock.Lock()
		delete(c.s.conns, c)
		c.s.connsLock.Unlock()
	}()

	for {
		msg := &msgpb.Message{}
		msgID, err := util.ReadMessage(c.rb, msg)
		if err != nil {
			log.Errorf("read request message err %v", err)
			return
		}

		if msg.GetMsgType() != msgpb.MessageType_PdReq {
			log.Errorf("invalid request message %v", msg)
			return
		}

		start := time.Now()
		request := msg.GetPdReq()
		label := metrics.GetCmdLabel(request)

		var response *pdpb.Response

		if !c.s.IsLeader() {
			response, err = c.proxyRequest(msgID, request)
			if err != nil {
				log.Errorf("proxy request %s err %v", request, errors.ErrorStack(err))
				response = newError(err)
			}
		} else {
			response, err = c.handleRequest(request)
			if err != nil {
				log.Errorf("handle request %s err %v", request, errors.ErrorStack(err))
				response = newError(err)

			}
		}

		if err == nil {
			cmdCounter.WithLabelValues(label).Inc()
			cmdDuration.WithLabelValues(label).Observe(time.Since(start).Seconds())
		} else {
			cmdFailedCounter.WithLabelValues(label).Inc()
			cmdFailedDuration.WithLabelValues(label).Observe(time.Since(start).Seconds())
		}

		if response == nil {
			// we don't need to response, maybe error?
			// if error, we will return an error response later.
			log.Warn("empty response")
			continue
		}

		updateResponse(request, response)

		msg = &msgpb.Message{
			MsgType: msgpb.MessageType_PdResp,
			PdResp:  response,
		}

		if err = util.WriteMessage(c.wb, msgID, msg); err != nil {
			log.Errorf("write response message err %v", err)
			return
		}

		if err = c.wb.Flush(); err != nil {
			log.Errorf("flush response message err %v", err)
			return
		}

		cmdCompletedDuration.WithLabelValues(label).Observe(time.Since(start).Seconds())
	}
}

func updateResponse(req *pdpb.Request, resp *pdpb.Response) {
	// We can use request field directly here.
	resp.CmdType = req.CmdType

	if req.Header == nil {
		return
	}

	if resp.Header == nil {
		resp.Header = &pdpb.ResponseHeader{}
	}

	resp.Header.Uuid = req.Header.Uuid
	resp.Header.ClusterId = req.Header.ClusterId
}

func (c *conn) close() error {
	if c.leaderConn != nil {
		c.leaderConn.Close()
	}
	return errors.Trace(c.conn.Close())
}

func (c *conn) proxyRequest(msgID uint64, req *pdpb.Request) (*pdpb.Response, error) {
	// Create a connection to leader.
	if c.leaderConn == nil {
		leader, err := c.s.GetLeader()
		if err != nil {
			return nil, errors.Trace(err)
		}
		if leader == nil {
			return nil, errors.New("no leader")
		}
		conn, err := rpcConnect(leader.GetAddr())
		if err != nil {
			return nil, errors.Trace(err)
		}
		c.leaderConn = conn
		log.Debugf("proxy conn %v to leader %s", c.conn.RemoteAddr(), leader.GetAddr())
	}

	resp, err := rpcCall(c.leaderConn, msgID, req)
	if err != nil {
		c.leaderConn.Close()
		c.leaderConn = nil
	}
	return resp, errors.Trace(err)
}

func (c *conn) handleRequest(req *pdpb.Request) (*pdpb.Response, error) {
	clusterID := req.GetHeader().GetClusterId()
	if clusterID != c.s.cfg.ClusterID {
		return nil, errors.Errorf("mismatch cluster id, need %d but got %d", c.s.cfg.ClusterID, clusterID)
	}

	switch req.GetCmdType() {
	case pdpb.CommandType_Tso:
		return c.handleTso(req)
	case pdpb.CommandType_AllocId:
		return c.handleAllocID(req)
	case pdpb.CommandType_Bootstrap:
		return c.handleBootstrap(req)
	case pdpb.CommandType_IsBootstrapped:
		return c.handleIsBootstrapped(req)
	case pdpb.CommandType_GetStore:
		return c.handleGetStore(req)
	case pdpb.CommandType_PutStore:
		return c.handlePutStore(req)
	case pdpb.CommandType_AskSplit:
		return c.handleAskSplit(req)
	case pdpb.CommandType_ReportSplit:
		return c.handleReportSplit(req)
	case pdpb.CommandType_GetRegion:
		return c.handleGetRegion(req)
	case pdpb.CommandType_RegionHeartbeat:
		return c.handleRegionHeartbeat(req)
	case pdpb.CommandType_StoreHeartbeat:
		return c.handleStoreHeartbeat(req)
	case pdpb.CommandType_GetClusterConfig:
		return c.handleGetClusterConfig(req)
	case pdpb.CommandType_PutClusterConfig:
		return c.handlePutClusterConfig(req)
	default:
		return nil, errors.Errorf("unsupported command %s", req)
	}
}
<commit_msg>fix data race. (#320)<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package server

import (
	"bufio"
	"net"
	"time"

	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/kvproto/pkg/msgpb"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"github.com/pingcap/kvproto/pkg/util"
	"github.com/pingcap/pd/pkg/metrics"
)

const (
	readBufferSize  = 8 * 1024
	writeBufferSize = 8 * 1024
)

type conn struct {
	s *Server

	rb   *bufio.Reader
	wb   *bufio.Writer
	conn net.Conn
}

func newConn(s *Server, netConn net.Conn, bufrw *bufio.ReadWriter) (*conn, error) {
	s.connsLock.Lock()
	defer s.connsLock.Unlock()

	c := &conn{
		s:    s,
		rb:   bufrw.Reader,
		wb:   bufrw.Writer,
		conn: netConn,
	}

	s.conns[c] = struct{}{}

	return c, nil
}

func (c *conn) run() {
	defer func() {
		c.s.wg.Done()
		c.close()

		c.s.connsLock.Lock()
		delete(c.s.conns, c)
		c.s.connsLock.Unlock()
	}()

	p := &leaderProxy{s: c.s}
	defer p.close()

	for {
		msg := &msgpb.Message{}
		msgID, err := util.ReadMessage(c.rb, msg)
		if err != nil {
			log.Errorf("read request message err %v", err)
			return
		}

		if msg.GetMsgType() != msgpb.MessageType_PdReq {
			log.Errorf("invalid request message %v", msg)
			return
		}

		start := time.Now()
		request := msg.GetPdReq()
		label := metrics.GetCmdLabel(request)

		var response *pdpb.Response

		if !c.s.IsLeader() {
			response, err = p.handleRequest(msgID, request)
			if err != nil {
				log.Errorf("proxy request %s err %v", request, errors.ErrorStack(err))
				response = newError(err)
			}
		} else {
			response, err = c.handleRequest(request)
			if err != nil {
				log.Errorf("handle request %s err %v", request, errors.ErrorStack(err))
				response = newError(err)

			}
		}

		if err == nil {
			cmdCounter.WithLabelValues(label).Inc()
			cmdDuration.WithLabelValues(label).Observe(time.Since(start).Seconds())
		} else {
			cmdFailedCounter.WithLabelValues(label).Inc()
			cmdFailedDuration.WithLabelValues(label).Observe(time.Since(start).Seconds())
		}

		if response == nil {
			// we don't need to response, maybe error?
			// if error, we will return an error response later.
			log.Warn("empty response")
			continue
		}

		updateResponse(request, response)

		msg = &msgpb.Message{
			MsgType: msgpb.MessageType_PdResp,
			PdResp:  response,
		}

		if err = util.WriteMessage(c.wb, msgID, msg); err != nil {
			log.Errorf("write response message err %v", err)
			return
		}

		if err = c.wb.Flush(); err != nil {
			log.Errorf("flush response message err %v", err)
			return
		}

		cmdCompletedDuration.WithLabelValues(label).Observe(time.Since(start).Seconds())
	}
}

func updateResponse(req *pdpb.Request, resp *pdpb.Response) {
	// We can use request field directly here.
	resp.CmdType = req.CmdType

	if req.Header == nil {
		return
	}

	if resp.Header == nil {
		resp.Header = &pdpb.ResponseHeader{}
	}

	resp.Header.Uuid = req.Header.Uuid
	resp.Header.ClusterId = req.Header.ClusterId
}

func (c *conn) close() error {
	return errors.Trace(c.conn.Close())
}

func (c *conn) handleRequest(req *pdpb.Request) (*pdpb.Response, error) {
	clusterID := req.GetHeader().GetClusterId()
	if clusterID != c.s.cfg.ClusterID {
		return nil, errors.Errorf("mismatch cluster id, need %d but got %d", c.s.cfg.ClusterID, clusterID)
	}

	switch req.GetCmdType() {
	case pdpb.CommandType_Tso:
		return c.handleTso(req)
	case pdpb.CommandType_AllocId:
		return c.handleAllocID(req)
	case pdpb.CommandType_Bootstrap:
		return c.handleBootstrap(req)
	case pdpb.CommandType_IsBootstrapped:
		return c.handleIsBootstrapped(req)
	case pdpb.CommandType_GetStore:
		return c.handleGetStore(req)
	case pdpb.CommandType_PutStore:
		return c.handlePutStore(req)
	case pdpb.CommandType_AskSplit:
		return c.handleAskSplit(req)
	case pdpb.CommandType_ReportSplit:
		return c.handleReportSplit(req)
	case pdpb.CommandType_GetRegion:
		return c.handleGetRegion(req)
	case pdpb.CommandType_RegionHeartbeat:
		return c.handleRegionHeartbeat(req)
	case pdpb.CommandType_StoreHeartbeat:
		return c.handleStoreHeartbeat(req)
	case pdpb.CommandType_GetClusterConfig:
		return c.handleGetClusterConfig(req)
	case pdpb.CommandType_PutClusterConfig:
		return c.handlePutClusterConfig(req)
	default:
		return nil, errors.Errorf("unsupported command %s", req)
	}
}

type leaderProxy struct {
	s    *Server
	conn net.Conn
}

func (p *leaderProxy) close() {
	if p.conn != nil {
		p.conn.Close()
	}
}

func (p *leaderProxy) handleRequest(msgID uint64, req *pdpb.Request) (*pdpb.Response, error) {
	// Create a connection to leader.
	if p.conn == nil {
		leader, err := p.s.GetLeader()
		if err != nil {
			return nil, errors.Trace(err)
		}
		if leader == nil {
			return nil, errors.New("no leader")
		}
		conn, err := rpcConnect(leader.GetAddr())
		if err != nil {
			return nil, errors.Trace(err)
		}
		p.conn = conn
	}

	resp, err := rpcCall(p.conn, msgID, req)
	if err != nil {
		p.conn.Close()
		p.conn = nil
	}
	return resp, errors.Trace(err)
}
<|endoftext|>


<commit_before>package main

import (
	"math"
	"strconv"
	"time"
)

type Game struct {
	// Registered clients.
	clients map[*Client]bool

	// Register requests from the clients.
	register chan *Client

	// Unregister requests from clients.
	unregister chan *Client

	// Game objects that currently exist
	history *GameHistory

	// Next valid game object id.
	nextId int
}

type GameSettings struct {
	// All the important game variables
	constants *GameConstants

	// Whether or not to print debug messages.
	debug bool
}

type GameConstants struct {
	ShipRadius       float64 `json:"ship_radius"`
	ShipAcceleration float64 `json:"ship_acceleration"`
	ShipRotation     float64 `json:"ship_rotation"`
	ShipDrag         float64 `json:"ship_drag"`
	ProjectileRadius float64 `json:"projectile_radius"`
	ProjectileSpeed  float64 `json:"projectile_speed"`
}

type GameError struct {
	What string
}

func (e GameError) Error() string {
	return e.What
}

const (
	gameUpdatePeriod = 10 * time.Millisecond
)

// TODO: Get this working without a global variable, I guess pass a ref to game into the web socket handler function?
var settings = &GameSettings{
	debug: false,

	// Game constants, values are all per-second
	constants: &GameConstants{
		ShipRadius:       5.6,  // Pixels
		ShipAcceleration: 100,  // Pixels per second^2
		ShipDrag:         -0.2, // Percentage reduction per second
		ShipRotation:     200,  // Degrees per second
		ProjectileRadius: 3,    // Pixels
		ProjectileSpeed:  180,  // Pixels per second
	},
}
var game = CreateGame()

func CreateGame() *Game {
	g := &Game{
		clients:    make(map[*Client]bool),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		history:    CreateGameHistory(),
	}

	// Create asteroids
	for i := 0; i < 100; i++ {
		id := g.generateId()
		geom := RandomAsteroidGeometry()
		g.history.Run(&CreateAsteroidEvent{MakeTimestamp(), id, geom})
	}

	return g
}

func (g *Game) Run() {
	gameUpdateTicker := time.NewTicker(gameUpdatePeriod)
	defer func() {
		gameUpdateTicker.Stop()
	}()

	for {
		select {
		case c := <-g.register:
			// Register client
			g.clients[c] = true

			// Create ship
			id := g.generateId()
			state := g.history.Run(&CreateShipEvent{MakeTimestamp(), id, MakePoint(0, 0)})

			// Send game state dump to player
			c.Initialize(id, settings.constants, state)
		case c := <-g.unregister:
			if _, ok := g.clients[c]; ok {
				delete(g.clients, c)
			}
		case <-gameUpdateTicker.C:
			g.history.Tick(g.lowestSeenUpdateTime())
			// state := g.history.Tick(g.lowestSeenUpdateTime())
			// if settings.debug {
			// 	log.Println(fmt.Sprintf("Ships: %d, Projectiles: %d", len(state.Ships), len(state.Projectiles)))
			// }
		}
	}
}

func (g *Game) generateId() string {
	g.nextId++
	return strconv.Itoa(g.nextId)
}

func (g *Game) lowestSeenUpdateTime() uint64 {
	var lowest uint64 = math.MaxUint64
	for c, _ := range g.clients {
		t := c.LastUpdateTime()
		if t < lowest {
			lowest = t
		}
	}
	return lowest
}
<commit_msg>Added a 2 second buffer period before removing dead objects, in case history is rewritten.<commit_after>package main

import (
	"math"
	"strconv"
	"time"
)

type Game struct {
	// Registered clients.
	clients map[*Client]bool

	// Register requests from the clients.
	register chan *Client

	// Unregister requests from clients.
	unregister chan *Client

	// Game objects that currently exist
	history *GameHistory

	// Next valid game object id.
	nextId int
}

type GameSettings struct {
	// All the important game variables
	constants *GameConstants

	// Whether or not to print debug messages.
	debug bool
}

type GameConstants struct {
	ShipRadius       float64 `json:"ship_radius"`
	ShipAcceleration float64 `json:"ship_acceleration"`
	ShipRotation     float64 `json:"ship_rotation"`
	ShipDrag         float64 `json:"ship_drag"`
	ProjectileRadius float64 `json:"projectile_radius"`
	ProjectileSpeed  float64 `json:"projectile_speed"`
}

type GameError struct {
	What string
}

func (e GameError) Error() string {
	return e.What
}

const (
	gameUpdatePeriod = 10 * time.Millisecond
	updateTimeBuffer = 2000 // in milliseconds
)

// TODO: Get this working without a global variable, I guess pass a ref to game into the web socket handler function?
var settings = &GameSettings{
	debug: false,

	// Game constants, values are all per-second
	constants: &GameConstants{
		ShipRadius:       5.6,  // Pixels
		ShipAcceleration: 100,  // Pixels per second^2
		ShipDrag:         -0.2, // Percentage reduction per second
		ShipRotation:     200,  // Degrees per second
		ProjectileRadius: 3,    // Pixels
		ProjectileSpeed:  180,  // Pixels per second
	},
}
var game = CreateGame()

func CreateGame() *Game {
	g := &Game{
		clients:    make(map[*Client]bool),
		register:   make(chan *Client),
		unregister: make(chan *Client),
		history:    CreateGameHistory(),
	}

	// Create asteroids
	for i := 0; i < 100; i++ {
		id := g.generateId()
		geom := RandomAsteroidGeometry()
		g.history.Run(&CreateAsteroidEvent{MakeTimestamp(), id, geom})
	}

	return g
}

func (g *Game) Run() {
	gameUpdateTicker := time.NewTicker(gameUpdatePeriod)
	defer func() {
		gameUpdateTicker.Stop()
	}()

	for {
		select {
		case c := <-g.register:
			// Register client
			g.clients[c] = true

			// Create ship
			id := g.generateId()
			state := g.history.Run(&CreateShipEvent{MakeTimestamp(), id, MakePoint(0, 0)})

			// Send game state dump to player
			c.Initialize(id, settings.constants, state)
		case c := <-g.unregister:
			if _, ok := g.clients[c]; ok {
				delete(g.clients, c)
			}
		case <-gameUpdateTicker.C:
			g.history.Tick(g.lowestSeenUpdateTime())
			// state := g.history.Tick(g.lowestSeenUpdateTime())
			// if settings.debug {
			// 	log.Println(fmt.Sprintf("Ships: %d, Projectiles: %d", len(state.Ships), len(state.Projectiles)))
			// }
		}
	}
}

func (g *Game) generateId() string {
	g.nextId++
	return strconv.Itoa(g.nextId)
}

func (g *Game) lowestSeenUpdateTime() uint64 {
	var lowest uint64 = math.MaxUint64
	for c, _ := range g.clients {
		t := c.LastUpdateTime() - updateTimeBuffer
		if t < lowest {
			lowest = t
		}
	}
	return lowest
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
)

type LinkLedger struct {
	Source struct {
		Id int `json:"id"`
	} `json:"source"` // the soure id
	Block struct {
		Id int `json:"id"`
	} `json:"block"` // the block id
	Id int `json:"id"` // link id
}

type ProtoLink struct {
	Source struct {
		Id int `json:"id"`
	} `json:"source"` // the soure id
	Block struct {
		Id int `json:"id"`
	} `json:"block"` // the block id
}

func (s *Server) CreateLink(l ProtoLink) (*LinkLedger, error) {
	b, ok := s.blocks[l.Block.Id]
	if !ok {
		return nil, errors.New("could not find block")
	}

	sl, ok := s.sources[l.Source.Id]
	if !ok {
		return nil, errors.New("could not find source")
	}

	link := &LinkLedger{}
	link.Id = s.GetNextID()
	link.Source.Id = l.Source.Id
	link.Block.Id = l.Block.Id

	if b.GetParent() != sl.GetParent() {
		return nil, errors.New("block and source must be in the same group, cannot link")
	}

	err := b.Block.SetSource(sl.Source)
	if err != nil {
		return nil, err
	}

	s.links[link.Id] = link

	s.websocketBroadcast(Update{Action: CREATE, Type: LINK, Data: wsLink{*link}})

	return link, nil
}

func (s *Server) DeleteLink(id int) error {
	link, ok := s.links[id]
	if !ok {
		return errors.New("could not find link")
	}

	s.blocks[link.Block.Id].Block.SetSource(nil)
	delete(s.links, id)

	s.websocketBroadcast(Update{Action: DELETE, Type: LINK, Data: wsLink{wsId{id}}})
	return nil
}

func (s *Server) ListLinks() []LinkLedger {
	links := []LinkLedger{}
	for _, l := range s.links {
		links = append(links, *l)
	}
	return links
}

func (s *Server) LinkIndexHandler(w http.ResponseWriter, r *http.Request) {
	s.Lock()
	defer s.Unlock()

	c := s.ListLinks()

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(c); err != nil {
		panic(err)
	}
}

func (s *Server) LinkCreateHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	var newLink ProtoLink
	json.Unmarshal(body, &newLink)

	s.Lock()
	defer s.Unlock()

	nl, err := s.CreateLink(newLink)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusOK)
	writeJSON(w, nl)
}

func (s *Server) LinkDeleteHandler(w http.ResponseWriter, r *http.Request) {
	id, err := getIDFromMux(mux.Vars(r))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, err)
		return
	}

	s.Lock()
	defer s.Unlock()

	err = s.DeleteLink(id)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
<commit_msg>consistency<commit_after>package server

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
)

type LinkLedger struct {
	Source struct {
		Id int `json:"id"`
	} `json:"source"` // the soure id
	Block struct {
		Id int `json:"id"`
	} `json:"block"` // the block id
	Id int `json:"id"` // link id
}

type ProtoLink struct {
	Source struct {
		Id int `json:"id"`
	} `json:"source"` // the soure id
	Block struct {
		Id int `json:"id"`
	} `json:"block"` // the block id
}

func (s *Server) CreateLink(l ProtoLink) (*LinkLedger, error) {
	b, ok := s.blocks[l.Block.Id]
	if !ok {
		return nil, errors.New("could not find block")
	}

	sl, ok := s.sources[l.Source.Id]
	if !ok {
		return nil, errors.New("could not find source")
	}

	link := &LinkLedger{}
	link.Id = s.GetNextID()
	link.Source.Id = l.Source.Id
	link.Block.Id = l.Block.Id

	if b.GetParent() != sl.GetParent() {
		return nil, errors.New("block and source must be in the same group, cannot link")
	}

	err := b.Block.SetSource(sl.Source)
	if err != nil {
		return nil, err
	}

	s.links[link.Id] = link

	s.websocketBroadcast(Update{Action: CREATE, Type: LINK, Data: wsLink{*link}})

	return link, nil
}

func (s *Server) DeleteLink(id int) error {
	link, ok := s.links[id]
	if !ok {
		return errors.New("could not find link")
	}

	s.blocks[link.Block.Id].Block.SetSource(nil)
	delete(s.links, id)

	s.websocketBroadcast(Update{Action: DELETE, Type: LINK, Data: wsLink{wsId{id}}})
	return nil
}

func (s *Server) listLinks() []LinkLedger {
	links := []LinkLedger{}
	for _, l := range s.links {
		links = append(links, *l)
	}
	return links
}

func (s *Server) LinkIndexHandler(w http.ResponseWriter, r *http.Request) {
	s.Lock()
	defer s.Unlock()

	c := s.listLinks()

	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(c); err != nil {
		panic(err)
	}
}

func (s *Server) LinkCreateHandler(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	var newLink ProtoLink
	json.Unmarshal(body, &newLink)

	s.Lock()
	defer s.Unlock()

	nl, err := s.CreateLink(newLink)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusOK)
	writeJSON(w, nl)
}

func (s *Server) LinkDeleteHandler(w http.ResponseWriter, r *http.Request) {
	id, err := getIDFromMux(mux.Vars(r))
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, err)
		return
	}

	s.Lock()
	defer s.Unlock()

	err = s.DeleteLink(id)
	if err != nil {
		w.WriteHeader(http.StatusBadRequest)
		writeJSON(w, Error{err.Error()})
		return
	}

	w.WriteHeader(http.StatusNoContent)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/signal"
	"syscall"
	"time"
)

type Packet struct {
	source  string
	arrived time.Time
	content []byte
}

var (
	Log = NewLogger(os.Stderr, LOG_DEBUG, 10*time.Second)
	// For input sentence or message "errors"
	AisLog = NewLogger(os.Stdout, LOG_DEBUG, 0)
)

func main() {
	signalChan := make(chan os.Signal, 1)
	// Intercept ^C and `timeout`s
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)

	Log.AddPeriodicLogger("source_connections", 20*time.Second, func(l *Logger, _ time.Duration) {
		l.Debug("source connections: %d", listener_connections)
	})
	packets := make(chan Packet, 200)
	send := make(chan string)
	readAIS(send)
	ecc := NewPacketHandler("ECC", packets)
	go ReadHTTP("http://aishub.ais.ecc.no/raw", 5*time.Second, ecc)
	kystverket := NewPacketHandler("Kystverket", packets)
	go ReadTCP("153.44.253.27:5631", 5*time.Second, kystverket)
	//test := NewPacketHandler("test", packets)
	//http.SourceName += "_timeout"
	//go ReadHTTP("http://127.0.0.1:12345", 8*time.Second, test)
	//tcp.SourceName += "_timeout"
	//go ReadTCP("test_timeout", "127.0.0.1:12345", 2*time.Second, test)
	//http.SourceName += " test_redirect"
	//go ReadHTTP("test_redirect", "http://localhost:12346", 0*time.Second, test)
	//loop := NewPacketHandler("test_redirect_loop", make(chan<- Packet))
	//go ReadHTTP("http://localhost:12347", 0*time.Second, loop)
	//test.SourceName = "file"
	//go ReadFile("minute_ecc.log", test)
	go func() {
		for packet := range packets {
			splitPacket(packet.content, send)
		}
	}()

	// Here we wait for CTRL-C or some other kill signal
	_ = <-signalChan
	Log.Info("\nPackets left: %d", len(packets))
	ecc.Log(Log)
	kystverket.Log(Log)
	//test.Log()
}
<commit_msg>Also catch SIGPIPE in signal handler<commit_after>package main

import (
	"os"
	"os/signal"
	"syscall"
	"time"
)

type Packet struct {
	source  string
	arrived time.Time
	content []byte
}

var (
	Log = NewLogger(os.Stderr, LOG_DEBUG, 10*time.Second)
	// For input sentence or message "errors"
	AisLog = NewLogger(os.Stdout, LOG_DEBUG, 0)
)

func main() {
	signalChan := make(chan os.Signal, 1)
	// Intercept ^C and `timeout`s.
	// Catching SIGPIPE has no effect if it was what Log wrote to that broke, as it's, well, broken.
	signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGPIPE)

	Log.AddPeriodicLogger("source_connections", 20*time.Second, func(l *Logger, _ time.Duration) {
		l.Debug("source connections: %d", listener_connections)
	})
	packets := make(chan Packet, 200)
	send := make(chan string)
	readAIS(send)
	ecc := NewPacketHandler("ECC", packets)
	go ReadHTTP("http://aishub.ais.ecc.no/raw", 5*time.Second, ecc)
	kystverket := NewPacketHandler("Kystverket", packets)
	go ReadTCP("153.44.253.27:5631", 5*time.Second, kystverket)
	//test := NewPacketHandler("test", packets)
	//http.SourceName += "_timeout"
	//go ReadHTTP("http://127.0.0.1:12345", 8*time.Second, test)
	//tcp.SourceName += "_timeout"
	//go ReadTCP("test_timeout", "127.0.0.1:12345", 2*time.Second, test)
	//http.SourceName += " test_redirect"
	//go ReadHTTP("test_redirect", "http://localhost:12346", 0*time.Second, test)
	//loop := NewPacketHandler("test_redirect_loop", make(chan<- Packet))
	//go ReadHTTP("http://localhost:12347", 0*time.Second, loop)
	//test.SourceName = "file"
	//go ReadFile("minute_ecc.log", test)
	go func() {
		for packet := range packets {
			splitPacket(packet.content, send)
		}
	}()

	// Here we wait for CTRL-C or some other kill signal
	_ = <-signalChan
	Log.Info("\nPackets left: %d", len(packets))
	ecc.Log(Log)
	kystverket.Log(Log)
	//test.Log()
}
<|endoftext|>


<commit_before>package main

import (
	// "flag"
	// "github.com/labstack/echo"
	// mw "github.com/labstack/echo/middleware"
	"github.com/tmaiaroto/discfg/config"
	// "github.com/tmaiaroto/discfg/version"
	"log"
)

var options = config.Options{StorageInterfaceName: "dynamodb", Version: version.Semantic}

func main() {
	// TODO: remove
	log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)

	// port := *flag.String("port", "8899", "API port")
	// apiVersion := *flag.String("version", "v1", "API version")
	// region := *flag.String("region", "us-east-1", "AWS region")

	// options.Storage.AWS.Region = region

	// e := echo.New()

	// // Middleware
	// //e.Use(mw.Logger())
	// e.Use(mw.Gzip())
	// e.Use(mw.Recover())

	// // Routes
	// switch apiVersion {
	// default:
	// case "v1":
	// 	v1Routes(e)
	// }

	// // Start server
	// e.Run(":" + port)
}
<commit_msg>making ci happy<commit_after>package main

import (
	// "flag"
	// "github.com/labstack/echo"
	// mw "github.com/labstack/echo/middleware"
	"github.com/tmaiaroto/discfg/config"
	"github.com/tmaiaroto/discfg/version"
	"log"
)

var options = config.Options{StorageInterfaceName: "dynamodb", Version: version.Semantic}

func main() {
	// TODO: remove
	log.SetFlags(log.Lshortfile | log.Ldate | log.Ltime)

	// port := *flag.String("port", "8899", "API port")
	// apiVersion := *flag.String("version", "v1", "API version")
	// region := *flag.String("region", "us-east-1", "AWS region")

	// options.Storage.AWS.Region = region

	// e := echo.New()

	// // Middleware
	// //e.Use(mw.Logger())
	// e.Use(mw.Gzip())
	// e.Use(mw.Recover())

	// // Routes
	// switch apiVersion {
	// default:
	// case "v1":
	// 	v1Routes(e)
	// }

	// // Start server
	// e.Run(":" + port)
}
<|endoftext|>


<commit_before>package server

import (
	"bufio"
	"errors"
	"github.com/siddontang/ledisdb/ledis"
	"io"
	"strconv"
)

var (
	errArrayFormat = errors.New("bad array format")
	errBulkFormat  = errors.New("bad bulk string format")
	errLineFormat  = errors.New("bad response line format")
)

func ReadLine(rb *bufio.Reader) ([]byte, error) {
	p, err := rb.ReadSlice('\n')

	if err != nil {
		return nil, err
	}
	i := len(p) - 2
	if i < 0 || p[i] != '\r' {
		return nil, errLineFormat
	}
	return p[:i], nil
}

func ReadBulkTo(rb *bufio.Reader, w io.Writer) error {
	l, err := ReadLine(rb)
	if len(l) == 0 {
		return errBulkFormat
	} else if l[0] == '$' {
		var n int
		//handle resp string
		if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
			return err
		} else if n == -1 {
			return nil
		} else {
			if _, err = io.CopyN(w, rb, int64(n)); err != nil {
				return err
			}

			if l, err = ReadLine(rb); err != nil {
				return err
			} else if len(l) != 0 {
				return errBulkFormat
			}
		}
	} else {
		return errBulkFormat
	}

	return nil
}
<commit_msg>update ReadBulkTo<commit_after>package server

import (
	"bufio"
	"errors"
	"github.com/siddontang/ledisdb/ledis"
	"io"
	"strconv"
)

var (
	errArrayFormat = errors.New("bad array format")
	errBulkFormat  = errors.New("bad bulk string format")
	errLineFormat  = errors.New("bad response line format")
)

func ReadLine(rb *bufio.Reader) ([]byte, error) {
	p, err := rb.ReadSlice('\n')

	if err != nil {
		return nil, err
	}
	i := len(p) - 2
	if i < 0 || p[i] != '\r' {
		return nil, errLineFormat
	}
	return p[:i], nil
}

func ReadBulkTo(rb *bufio.Reader, w io.Writer) error {
	l, err := ReadLine(rb)
	if err != nil {
		return err
	} else if len(l) == 0 {
		return errBulkFormat
	} else if l[0] == '$' {
		var n int
		//handle resp string
		if n, err = strconv.Atoi(ledis.String(l[1:])); err != nil {
			return err
		} else if n == -1 {
			return nil
		} else {
			var nn int64
			if nn, err = io.CopyN(w, rb, int64(n)); err != nil {
				return err
			} else if nn != int64(n) {
				return io.ErrShortWrite
			}

			if l, err = ReadLine(rb); err != nil {
				return err
			} else if len(l) != 0 {
				return errBulkFormat
			}
		}
	} else {
		return errBulkFormat
	}

	return nil
}
<|endoftext|>


<commit_before>/*
Copyright 2019 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 log will be removed after switching to use core framework log.
// Do not make further changes here!
package log

import (
	"fmt"
	"time"

	"github.com/onsi/ginkgo"

	"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
)

func nowStamp() string {
	return time.Now().Format(time.StampMilli)
}

func log(level string, format string, args ...interface{}) {
	fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...)
}

// Logf logs the info.
func Logf(format string, args ...interface{}) {
	log("INFO", format, args...)
}

// Failf logs the fail info.
func Failf(format string, args ...interface{}) {
	FailfWithOffset(1, format, args...)
}

// FailfWithOffset calls "Fail" and logs the error at "offset" levels above its caller
// (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f").
func FailfWithOffset(offset int, format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	log("FAIL", msg)
	ginkgowrapper.Fail(nowStamp()+": "+msg, 1+offset)
}

// Fail is a replacement for ginkgo.Fail which logs the problem as it occurs
// and then calls ginkgowrapper.Fail.
func Fail(msg string, callerSkip ...int) {
	skip := 1
	if len(callerSkip) > 0 {
		skip += callerSkip[0]
	}
	log("FAIL", msg)
	ginkgowrapper.Fail(nowStamp()+": "+msg, skip)
}
<commit_msg>e2e: remove unused method in e2e/framework/log<commit_after>/*
Copyright 2019 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 log will be removed after switching to use core framework log.
// Do not make further changes here!
package log

import (
	"fmt"
	"time"

	"github.com/onsi/ginkgo"

	"k8s.io/kubernetes/test/e2e/framework/ginkgowrapper"
)

func nowStamp() string {
	return time.Now().Format(time.StampMilli)
}

func log(level string, format string, args ...interface{}) {
	fmt.Fprintf(ginkgo.GinkgoWriter, nowStamp()+": "+level+": "+format+"\n", args...)
}

// Logf logs the info.
func Logf(format string, args ...interface{}) {
	log("INFO", format, args...)
}

// Failf logs the fail info.
func Failf(format string, args ...interface{}) {
	FailfWithOffset(1, format, args...)
}

// FailfWithOffset calls "Fail" and logs the error at "offset" levels above its caller
// (for example, for call chain f -> g -> FailfWithOffset(1, ...) error would be logged for "f").
func FailfWithOffset(offset int, format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	log("FAIL", msg)
	ginkgowrapper.Fail(nowStamp()+": "+msg, 1+offset)
}
<|endoftext|>


<commit_before>/*
Copyright 2014 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 framework

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
)

func EtcdUpgrade(target_storage, target_version string) error {
	switch TestContext.Provider {
	case "gce":
		return etcdUpgradeGCE(target_storage, target_version)
	default:
		return fmt.Errorf("EtcdUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
}

func IngressUpgrade(isUpgrade bool) error {
	switch TestContext.Provider {
	case "gce":
		return ingressUpgradeGCE(isUpgrade)
	default:
		return fmt.Errorf("IngressUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
}

func MasterUpgrade(v string) error {
	switch TestContext.Provider {
	case "gce":
		return masterUpgradeGCE(v, false)
	case "gke":
		return masterUpgradeGKE(v)
	case "kubernetes-anywhere":
		return masterUpgradeKubernetesAnywhere(v)
	default:
		return fmt.Errorf("MasterUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
}

func etcdUpgradeGCE(target_storage, target_version string) error {
	env := append(
		os.Environ(),
		"TEST_ETCD_VERSION="+target_version,
		"STORAGE_BACKEND="+target_storage,
		"TEST_ETCD_IMAGE=3.2.16")

	_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-l", "-M")
	return err
}

func ingressUpgradeGCE(isUpgrade bool) error {
	var command string
	if isUpgrade {
		// User specified image to upgrade to.
		targetImage := TestContext.IngressUpgradeImage
		if targetImage != "" {
			command = fmt.Sprintf("sudo sed -i -re 's|(image:)(.*)|\\1 %s|' /etc/kubernetes/manifests/glbc.manifest", targetImage)
		} else {
			// Upgrade to latest HEAD image.
			command = "sudo sed -i -re 's/(image:)(.*)/\\1 gcr.io\\/k8s-ingress-image-push\\/ingress-gce-e2e-glbc-amd64:latest/' /etc/kubernetes/manifests/glbc.manifest"
		}
	} else {
		// Downgrade to latest release image.
		command = "sudo sed -i -re 's/(image:)(.*)/\\1 k8s.gcr.io\\/google_containers\\/glbc:0.9.7/' /etc/kubernetes/manifests/glbc.manifest"
	}
	// Kubelet should restart glbc automatically.
	sshResult, err := NodeExec(GetMasterHost(), command)
	LogSSHResult(sshResult)
	return err
}

// TODO(mrhohn): Remove this function when kube-proxy is run as a DaemonSet by default.
func MasterUpgradeGCEWithKubeProxyDaemonSet(v string, enableKubeProxyDaemonSet bool) error {
	return masterUpgradeGCE(v, enableKubeProxyDaemonSet)
}

// TODO(mrhohn): Remove 'enableKubeProxyDaemonSet' when kube-proxy is run as a DaemonSet by default.
func masterUpgradeGCE(rawV string, enableKubeProxyDaemonSet bool) error {
	env := append(os.Environ(), fmt.Sprintf("KUBE_PROXY_DAEMONSET=%v", enableKubeProxyDaemonSet))
	// TODO: Remove these variables when they're no longer needed for downgrades.
	if TestContext.EtcdUpgradeVersion != "" && TestContext.EtcdUpgradeStorage != "" {
		env = append(env,
			"TEST_ETCD_VERSION="+TestContext.EtcdUpgradeVersion,
			"STORAGE_BACKEND="+TestContext.EtcdUpgradeStorage,
			"TEST_ETCD_IMAGE=3.2.16")
	} else {
		// In e2e tests, we skip the confirmation prompt about
		// implicit etcd upgrades to simulate the user entering "y".
		env = append(env, "TEST_ALLOW_IMPLICIT_ETCD_UPGRADE=true")
	}

	v := "v" + rawV
	_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-M", v)
	return err
}

func masterUpgradeGKE(v string) error {
	Logf("Upgrading master to %q", v)
	_, _, err := RunCmd("gcloud", "container",
		"clusters",
		fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID),
		fmt.Sprintf("--zone=%s", TestContext.CloudConfig.Zone),
		"upgrade",
		TestContext.CloudConfig.Cluster,
		"--master",
		fmt.Sprintf("--cluster-version=%s", v),
		"--quiet")
	if err != nil {
		return err
	}

	waitForSSHTunnels()

	return nil
}

func masterUpgradeKubernetesAnywhere(v string) error {
	Logf("Upgrading master to %q", v)

	kaPath := TestContext.KubernetesAnywherePath
	originalConfigPath := filepath.Join(kaPath, ".config")
	backupConfigPath := filepath.Join(kaPath, ".config.bak")
	updatedConfigPath := filepath.Join(kaPath, fmt.Sprintf(".config-%s", v))

	// modify config with specified k8s version
	if _, _, err := RunCmd("sed",
		"-i.bak", // writes original to .config.bak
		fmt.Sprintf(`s/kubernetes_version=.*$/kubernetes_version=%q/`, v),
		originalConfigPath); err != nil {
		return err
	}

	defer func() {
		// revert .config.bak to .config
		if err := os.Rename(backupConfigPath, originalConfigPath); err != nil {
			Logf("Could not rename %s back to %s", backupConfigPath, originalConfigPath)
		}
	}()

	// invoke ka upgrade
	if _, _, err := RunCmd("make", "-C", TestContext.KubernetesAnywherePath,
		"WAIT_FOR_KUBECONFIG=y", "upgrade-master"); err != nil {
		return err
	}

	// move .config to .config.<version>
	if err := os.Rename(originalConfigPath, updatedConfigPath); err != nil {
		return err
	}

	return nil
}

func NodeUpgrade(f *Framework, v string, img string) error {
	// Perform the upgrade.
	var err error
	switch TestContext.Provider {
	case "gce":
		err = nodeUpgradeGCE(v, img, false)
	case "gke":
		err = nodeUpgradeGKE(v, img)
	default:
		err = fmt.Errorf("NodeUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
	if err != nil {
		return err
	}

	// Wait for it to complete and validate nodes are healthy.
	//
	// TODO(ihmccreery) We shouldn't have to wait for nodes to be ready in
	// GKE; the operation shouldn't return until they all are.
	Logf("Waiting up to %v for all nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout)
	if _, err := CheckNodesReady(f.ClientSet, RestartNodeReadyAgainTimeout, TestContext.CloudConfig.NumNodes); err != nil {
		return err
	}
	return nil
}

// TODO(mrhohn): Remove this function when kube-proxy is run as a DaemonSet by default.
func NodeUpgradeGCEWithKubeProxyDaemonSet(f *Framework, v string, img string, enableKubeProxyDaemonSet bool) error {
	// Perform the upgrade.
	if err := nodeUpgradeGCE(v, img, enableKubeProxyDaemonSet); err != nil {
		return err
	}
	// Wait for it to complete and validate nodes are healthy.
	Logf("Waiting up to %v for all nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout)
	if _, err := CheckNodesReady(f.ClientSet, RestartNodeReadyAgainTimeout, TestContext.CloudConfig.NumNodes); err != nil {
		return err
	}
	return nil
}

// TODO(mrhohn): Remove 'enableKubeProxyDaemonSet' when kube-proxy is run as a DaemonSet by default.
func nodeUpgradeGCE(rawV, img string, enableKubeProxyDaemonSet bool) error {
	v := "v" + rawV
	env := append(os.Environ(), fmt.Sprintf("KUBE_PROXY_DAEMONSET=%v", enableKubeProxyDaemonSet))
	if img != "" {
		env = append(env, "KUBE_NODE_OS_DISTRIBUTION="+img)
		_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-N", "-o", v)
		return err
	}
	_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-N", v)
	return err
}

func nodeUpgradeGKE(v string, img string) error {
	Logf("Upgrading nodes to version %q and image %q", v, img)
	args := []string{
		"container",
		"clusters",
		fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID),
		fmt.Sprintf("--zone=%s", TestContext.CloudConfig.Zone),
		"upgrade",
		TestContext.CloudConfig.Cluster,
		fmt.Sprintf("--cluster-version=%s", v),
		"--quiet",
	}
	if len(img) > 0 {
		args = append(args, fmt.Sprintf("--image-type=%s", img))
	}
	_, _, err := RunCmd("gcloud", args...)

	if err != nil {
		return err
	}

	waitForSSHTunnels()

	return nil
}

// CheckNodesReady waits up to nt for expect nodes accessed by c to be ready,
// returning an error if this doesn't happen in time. It returns the names of
// nodes it finds.
func CheckNodesReady(c clientset.Interface, nt time.Duration, expect int) ([]string, error) {
	// First, keep getting all of the nodes until we get the number we expect.
	var nodeList *v1.NodeList
	var errLast error
	start := time.Now()
	found := wait.Poll(Poll, nt, func() (bool, error) {
		// A rolling-update (GCE/GKE implementation of restart) can complete before the apiserver
		// knows about all of the nodes. Thus, we retry the list nodes call
		// until we get the expected number of nodes.
		nodeList, errLast = c.CoreV1().Nodes().List(metav1.ListOptions{
			FieldSelector: fields.Set{"spec.unschedulable": "false"}.AsSelector().String()})
		if errLast != nil {
			return false, nil
		}
		if len(nodeList.Items) != expect {
			errLast = fmt.Errorf("expected to find %d nodes but found only %d (%v elapsed)",
				expect, len(nodeList.Items), time.Since(start))
			Logf("%v", errLast)
			return false, nil
		}
		return true, nil
	}) == nil
	nodeNames := make([]string, len(nodeList.Items))
	for i, n := range nodeList.Items {
		nodeNames[i] = n.ObjectMeta.Name
	}
	if !found {
		return nodeNames, fmt.Errorf("couldn't find %d nodes within %v; last error: %v",
			expect, nt, errLast)
	}
	Logf("Successfully found %d nodes", expect)

	// Next, ensure in parallel that all the nodes are ready. We subtract the
	// time we spent waiting above.
	timeout := nt - time.Since(start)
	result := make(chan bool, len(nodeList.Items))
	for _, n := range nodeNames {
		n := n
		go func() { result <- WaitForNodeToBeReady(c, n, timeout) }()
	}
	failed := false
	// TODO(mbforbes): Change to `for range` syntax once we support only Go
	// >= 1.4.
	for i := range nodeList.Items {
		_ = i
		if !<-result {
			failed = true
		}
	}
	if failed {
		return nodeNames, fmt.Errorf("at least one node failed to be ready")
	}
	return nodeNames, nil
}

// MigTemplate (GCE-only) returns the name of the MIG template that the
// nodes of the cluster use.
func MigTemplate() (string, error) {
	var errLast error
	var templ string
	key := "instanceTemplate"
	if wait.Poll(Poll, SingleCallTimeout, func() (bool, error) {
		// TODO(mikedanese): make this hit the compute API directly instead of
		// shelling out to gcloud.
		// An `instance-groups managed describe` call outputs what we want to stdout.
		output, _, err := retryCmd("gcloud", "compute", "instance-groups", "managed",
			fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID),
			"describe",
			fmt.Sprintf("--zone=%s", TestContext.CloudConfig.Zone),
			TestContext.CloudConfig.NodeInstanceGroup)
		if err != nil {
			errLast = fmt.Errorf("gcloud compute instance-groups managed describe call failed with err: %v", err)
			return false, nil
		}

		// The 'describe' call probably succeeded; parse the output and try to
		// find the line that looks like "instanceTemplate: url/to/<templ>" and
		// return <templ>.
		if val := ParseKVLines(output, key); len(val) > 0 {
			url := strings.Split(val, "/")
			templ = url[len(url)-1]
			Logf("MIG group %s using template: %s", TestContext.CloudConfig.NodeInstanceGroup, templ)
			return true, nil
		}
		errLast = fmt.Errorf("couldn't find %s in output to get MIG template. Output: %s", key, output)
		return false, nil
	}) != nil {
		return "", fmt.Errorf("MigTemplate() failed with last error: %v", errLast)
	}
	return templ, nil
}

func gceUpgradeScript() string {
	if len(TestContext.GCEUpgradeScript) == 0 {
		return path.Join(TestContext.RepoRoot, "cluster/gce/upgrade.sh")
	}
	return TestContext.GCEUpgradeScript
}

func waitForSSHTunnels() {
	Logf("Waiting for SSH tunnels to establish")
	RunKubectl("run", "ssh-tunnel-test",
		"--image=busybox",
		"--restart=Never",
		"--command", "--",
		"echo", "Hello")
	defer RunKubectl("delete", "pod", "ssh-tunnel-test")

	// allow up to a minute for new ssh tunnels to establish
	wait.PollImmediate(5*time.Second, time.Minute, func() (bool, error) {
		_, err := RunKubectl("logs", "ssh-tunnel-test")
		return err == nil, nil
	})
}
<commit_msg>Fix upgrade tests for GKE Regional Clusters<commit_after>/*
Copyright 2014 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 framework

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/util/wait"
	clientset "k8s.io/client-go/kubernetes"
)

func EtcdUpgrade(target_storage, target_version string) error {
	switch TestContext.Provider {
	case "gce":
		return etcdUpgradeGCE(target_storage, target_version)
	default:
		return fmt.Errorf("EtcdUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
}

func IngressUpgrade(isUpgrade bool) error {
	switch TestContext.Provider {
	case "gce":
		return ingressUpgradeGCE(isUpgrade)
	default:
		return fmt.Errorf("IngressUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
}

func MasterUpgrade(v string) error {
	switch TestContext.Provider {
	case "gce":
		return masterUpgradeGCE(v, false)
	case "gke":
		return masterUpgradeGKE(v)
	case "kubernetes-anywhere":
		return masterUpgradeKubernetesAnywhere(v)
	default:
		return fmt.Errorf("MasterUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
}

func etcdUpgradeGCE(target_storage, target_version string) error {
	env := append(
		os.Environ(),
		"TEST_ETCD_VERSION="+target_version,
		"STORAGE_BACKEND="+target_storage,
		"TEST_ETCD_IMAGE=3.2.16")

	_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-l", "-M")
	return err
}

func ingressUpgradeGCE(isUpgrade bool) error {
	var command string
	if isUpgrade {
		// User specified image to upgrade to.
		targetImage := TestContext.IngressUpgradeImage
		if targetImage != "" {
			command = fmt.Sprintf("sudo sed -i -re 's|(image:)(.*)|\\1 %s|' /etc/kubernetes/manifests/glbc.manifest", targetImage)
		} else {
			// Upgrade to latest HEAD image.
			command = "sudo sed -i -re 's/(image:)(.*)/\\1 gcr.io\\/k8s-ingress-image-push\\/ingress-gce-e2e-glbc-amd64:latest/' /etc/kubernetes/manifests/glbc.manifest"
		}
	} else {
		// Downgrade to latest release image.
		command = "sudo sed -i -re 's/(image:)(.*)/\\1 k8s.gcr.io\\/google_containers\\/glbc:0.9.7/' /etc/kubernetes/manifests/glbc.manifest"
	}
	// Kubelet should restart glbc automatically.
	sshResult, err := NodeExec(GetMasterHost(), command)
	LogSSHResult(sshResult)
	return err
}

// TODO(mrhohn): Remove this function when kube-proxy is run as a DaemonSet by default.
func MasterUpgradeGCEWithKubeProxyDaemonSet(v string, enableKubeProxyDaemonSet bool) error {
	return masterUpgradeGCE(v, enableKubeProxyDaemonSet)
}

// TODO(mrhohn): Remove 'enableKubeProxyDaemonSet' when kube-proxy is run as a DaemonSet by default.
func masterUpgradeGCE(rawV string, enableKubeProxyDaemonSet bool) error {
	env := append(os.Environ(), fmt.Sprintf("KUBE_PROXY_DAEMONSET=%v", enableKubeProxyDaemonSet))
	// TODO: Remove these variables when they're no longer needed for downgrades.
	if TestContext.EtcdUpgradeVersion != "" && TestContext.EtcdUpgradeStorage != "" {
		env = append(env,
			"TEST_ETCD_VERSION="+TestContext.EtcdUpgradeVersion,
			"STORAGE_BACKEND="+TestContext.EtcdUpgradeStorage,
			"TEST_ETCD_IMAGE=3.2.16")
	} else {
		// In e2e tests, we skip the confirmation prompt about
		// implicit etcd upgrades to simulate the user entering "y".
		env = append(env, "TEST_ALLOW_IMPLICIT_ETCD_UPGRADE=true")
	}

	v := "v" + rawV
	_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-M", v)
	return err
}

func locationParamGKE() string {
	if TestContext.CloudConfig.Zone != "" {
		return fmt.Sprintf("--zone=%s", TestContext.CloudConfig.Zone)
	}
	return fmt.Sprintf("--region=%s", TestContext.CloudConfig.Region)
}

func appendContainerCommandGroupIfNeeded(args []string) []string {
	if TestContext.CloudConfig.Region != "" {
		// TODO(wojtek-t): Get rid of it once Regional Clusters go to GA.
		return append([]string{"beta"}, args...)
	}
	return args
}

func masterUpgradeGKE(v string) error {
	Logf("Upgrading master to %q", v)
	args := []string{
		"container",
		"clusters",
		fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID),
		locationParamGKE(),
		"upgrade",
		TestContext.CloudConfig.Cluster,
		"--master",
		fmt.Sprintf("--cluster-version=%s", v),
		"--quiet",
	}
	_, _, err := RunCmd("gcloud", appendContainerCommandGroupIfNeeded(args)...)
	if err != nil {
		return err
	}

	waitForSSHTunnels()

	return nil
}

func masterUpgradeKubernetesAnywhere(v string) error {
	Logf("Upgrading master to %q", v)

	kaPath := TestContext.KubernetesAnywherePath
	originalConfigPath := filepath.Join(kaPath, ".config")
	backupConfigPath := filepath.Join(kaPath, ".config.bak")
	updatedConfigPath := filepath.Join(kaPath, fmt.Sprintf(".config-%s", v))

	// modify config with specified k8s version
	if _, _, err := RunCmd("sed",
		"-i.bak", // writes original to .config.bak
		fmt.Sprintf(`s/kubernetes_version=.*$/kubernetes_version=%q/`, v),
		originalConfigPath); err != nil {
		return err
	}

	defer func() {
		// revert .config.bak to .config
		if err := os.Rename(backupConfigPath, originalConfigPath); err != nil {
			Logf("Could not rename %s back to %s", backupConfigPath, originalConfigPath)
		}
	}()

	// invoke ka upgrade
	if _, _, err := RunCmd("make", "-C", TestContext.KubernetesAnywherePath,
		"WAIT_FOR_KUBECONFIG=y", "upgrade-master"); err != nil {
		return err
	}

	// move .config to .config.<version>
	if err := os.Rename(originalConfigPath, updatedConfigPath); err != nil {
		return err
	}

	return nil
}

func NodeUpgrade(f *Framework, v string, img string) error {
	// Perform the upgrade.
	var err error
	switch TestContext.Provider {
	case "gce":
		err = nodeUpgradeGCE(v, img, false)
	case "gke":
		err = nodeUpgradeGKE(v, img)
	default:
		err = fmt.Errorf("NodeUpgrade() is not implemented for provider %s", TestContext.Provider)
	}
	if err != nil {
		return err
	}

	// Wait for it to complete and validate nodes are healthy.
	//
	// TODO(ihmccreery) We shouldn't have to wait for nodes to be ready in
	// GKE; the operation shouldn't return until they all are.
	Logf("Waiting up to %v for all nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout)
	if _, err := CheckNodesReady(f.ClientSet, RestartNodeReadyAgainTimeout, TestContext.CloudConfig.NumNodes); err != nil {
		return err
	}
	return nil
}

// TODO(mrhohn): Remove this function when kube-proxy is run as a DaemonSet by default.
func NodeUpgradeGCEWithKubeProxyDaemonSet(f *Framework, v string, img string, enableKubeProxyDaemonSet bool) error {
	// Perform the upgrade.
	if err := nodeUpgradeGCE(v, img, enableKubeProxyDaemonSet); err != nil {
		return err
	}
	// Wait for it to complete and validate nodes are healthy.
	Logf("Waiting up to %v for all nodes to be ready after the upgrade", RestartNodeReadyAgainTimeout)
	if _, err := CheckNodesReady(f.ClientSet, RestartNodeReadyAgainTimeout, TestContext.CloudConfig.NumNodes); err != nil {
		return err
	}
	return nil
}

// TODO(mrhohn): Remove 'enableKubeProxyDaemonSet' when kube-proxy is run as a DaemonSet by default.
func nodeUpgradeGCE(rawV, img string, enableKubeProxyDaemonSet bool) error {
	v := "v" + rawV
	env := append(os.Environ(), fmt.Sprintf("KUBE_PROXY_DAEMONSET=%v", enableKubeProxyDaemonSet))
	if img != "" {
		env = append(env, "KUBE_NODE_OS_DISTRIBUTION="+img)
		_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-N", "-o", v)
		return err
	}
	_, _, err := RunCmdEnv(env, gceUpgradeScript(), "-N", v)
	return err
}

func nodeUpgradeGKE(v string, img string) error {
	Logf("Upgrading nodes to version %q and image %q", v, img)
	args := []string{
		"container",
		"clusters",
		fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID),
		locationParamGKE(),
		"upgrade",
		TestContext.CloudConfig.Cluster,
		fmt.Sprintf("--cluster-version=%s", v),
		"--quiet",
	}
	if len(img) > 0 {
		args = append(args, fmt.Sprintf("--image-type=%s", img))
	}
	_, _, err := RunCmd("gcloud", appendContainerCommandGroupIfNeeded(args)...)

	if err != nil {
		return err
	}

	waitForSSHTunnels()

	return nil
}

// CheckNodesReady waits up to nt for expect nodes accessed by c to be ready,
// returning an error if this doesn't happen in time. It returns the names of
// nodes it finds.
func CheckNodesReady(c clientset.Interface, nt time.Duration, expect int) ([]string, error) {
	// First, keep getting all of the nodes until we get the number we expect.
	var nodeList *v1.NodeList
	var errLast error
	start := time.Now()
	found := wait.Poll(Poll, nt, func() (bool, error) {
		// A rolling-update (GCE/GKE implementation of restart) can complete before the apiserver
		// knows about all of the nodes. Thus, we retry the list nodes call
		// until we get the expected number of nodes.
		nodeList, errLast = c.CoreV1().Nodes().List(metav1.ListOptions{
			FieldSelector: fields.Set{"spec.unschedulable": "false"}.AsSelector().String()})
		if errLast != nil {
			return false, nil
		}
		if len(nodeList.Items) != expect {
			errLast = fmt.Errorf("expected to find %d nodes but found only %d (%v elapsed)",
				expect, len(nodeList.Items), time.Since(start))
			Logf("%v", errLast)
			return false, nil
		}
		return true, nil
	}) == nil
	nodeNames := make([]string, len(nodeList.Items))
	for i, n := range nodeList.Items {
		nodeNames[i] = n.ObjectMeta.Name
	}
	if !found {
		return nodeNames, fmt.Errorf("couldn't find %d nodes within %v; last error: %v",
			expect, nt, errLast)
	}
	Logf("Successfully found %d nodes", expect)

	// Next, ensure in parallel that all the nodes are ready. We subtract the
	// time we spent waiting above.
	timeout := nt - time.Since(start)
	result := make(chan bool, len(nodeList.Items))
	for _, n := range nodeNames {
		n := n
		go func() { result <- WaitForNodeToBeReady(c, n, timeout) }()
	}
	failed := false
	// TODO(mbforbes): Change to `for range` syntax once we support only Go
	// >= 1.4.
	for i := range nodeList.Items {
		_ = i
		if !<-result {
			failed = true
		}
	}
	if failed {
		return nodeNames, fmt.Errorf("at least one node failed to be ready")
	}
	return nodeNames, nil
}

// MigTemplate (GCE-only) returns the name of the MIG template that the
// nodes of the cluster use.
func MigTemplate() (string, error) {
	var errLast error
	var templ string
	key := "instanceTemplate"
	if wait.Poll(Poll, SingleCallTimeout, func() (bool, error) {
		// TODO(mikedanese): make this hit the compute API directly instead of
		// shelling out to gcloud.
		// An `instance-groups managed describe` call outputs what we want to stdout.
		output, _, err := retryCmd("gcloud", "compute", "instance-groups", "managed",
			fmt.Sprintf("--project=%s", TestContext.CloudConfig.ProjectID),
			"describe",
			fmt.Sprintf("--zone=%s", TestContext.CloudConfig.Zone),
			TestContext.CloudConfig.NodeInstanceGroup)
		if err != nil {
			errLast = fmt.Errorf("gcloud compute instance-groups managed describe call failed with err: %v", err)
			return false, nil
		}

		// The 'describe' call probably succeeded; parse the output and try to
		// find the line that looks like "instanceTemplate: url/to/<templ>" and
		// return <templ>.
		if val := ParseKVLines(output, key); len(val) > 0 {
			url := strings.Split(val, "/")
			templ = url[len(url)-1]
			Logf("MIG group %s using template: %s", TestContext.CloudConfig.NodeInstanceGroup, templ)
			return true, nil
		}
		errLast = fmt.Errorf("couldn't find %s in output to get MIG template. Output: %s", key, output)
		return false, nil
	}) != nil {
		return "", fmt.Errorf("MigTemplate() failed with last error: %v", errLast)
	}
	return templ, nil
}

func gceUpgradeScript() string {
	if len(TestContext.GCEUpgradeScript) == 0 {
		return path.Join(TestContext.RepoRoot, "cluster/gce/upgrade.sh")
	}
	return TestContext.GCEUpgradeScript
}

func waitForSSHTunnels() {
	Logf("Waiting for SSH tunnels to establish")
	RunKubectl("run", "ssh-tunnel-test",
		"--image=busybox",
		"--restart=Never",
		"--command", "--",
		"echo", "Hello")
	defer RunKubectl("delete", "pod", "ssh-tunnel-test")

	// allow up to a minute for new ssh tunnels to establish
	wait.PollImmediate(5*time.Second, time.Minute, func() (bool, error) {
		_, err := RunKubectl("logs", "ssh-tunnel-test")
		return err == nil, nil
	})
}
<|endoftext|>


<commit_before>package e2e

import (
	"context"
	"testing"

	argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
	"github.com/stretchr/testify/assert"

	repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
	"github.com/argoproj/argo-cd/test/e2e/fixture"
	"github.com/argoproj/argo-cd/test/e2e/fixture/app"
	"github.com/argoproj/argo-cd/test/e2e/fixture/repos"
	"github.com/argoproj/argo-cd/util/settings"
)

func TestAddRemovePublicRepo(t *testing.T) {
	app.Given(t).And(func() {
		repoUrl := fixture.RepoURL(fixture.RepoURLTypeFile)
		_, err := fixture.RunCli("repo", "add", repoUrl)
		assert.NoError(t, err)

		conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
		assert.NoError(t, err)
		defer argoio.Close(conn)

		repo, err := repoClient.List(context.Background(), &repositorypkg.RepoQuery{})

		assert.Nil(t, err)
		exists := false
		for i := range repo.Items {
			if repo.Items[i].Repo == repoUrl {
				exists = true
				break
			}
		}
		assert.True(t, exists)

		_, err = fixture.RunCli("repo", "rm", repoUrl)
		assert.NoError(t, err)

		repo, err = repoClient.List(context.Background(), &repositorypkg.RepoQuery{})
		assert.NoError(t, err)
		exists = false
		for i := range repo.Items {
			if repo.Items[i].Repo == repoUrl {
				exists = true
				break
			}
		}
		assert.False(t, exists)
	})
}

func TestUpsertExistingRepo(t *testing.T) {
	app.Given(t).And(func() {
		fixture.SetRepos(settings.RepositoryCredentials{URL: fixture.RepoURL(fixture.RepoURLTypeFile)})
		repoUrl := fixture.RepoURL(fixture.RepoURLTypeFile)
		_, err := fixture.RunCli("repo", "add", repoUrl)
		assert.NoError(t, err)

		_, err = fixture.RunCli("repo", "add", repoUrl, "--username", fixture.GitUsername, "--password", fixture.GitPassword)
		assert.Error(t, err)

		_, err = fixture.RunCli("repo", "add", repoUrl, "--upsert", "--username", fixture.GitUsername, "--password", fixture.GitPassword)
		assert.NoError(t, err)
	})
}

func TestAddRemoveHelmRepo(t *testing.T) {
	app.Given(t).CustomCACertAdded().And(func() {
		_, err := fixture.RunCli("repo", "add", fixture.RepoURL(fixture.RepoURLTypeHelm),
			"--name", "testrepo",
			"--type", "helm",
			"--username", fixture.GitUsername,
			"--password", fixture.GitPassword,
			"--tls-client-cert-path", repos.CertPath,
			"--tls-client-cert-key-path", repos.CertKeyPath)
		assert.NoError(t, err)

		conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
		assert.NoError(t, err)
		defer argoio.Close(conn)

		repo, err := repoClient.List(context.Background(), &repositorypkg.RepoQuery{})

		assert.NoError(t, err)
		exists := false
		for i := range repo.Items {
			if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) {
				exists = true
				break
			}
		}
		assert.True(t, exists)

		_, err = fixture.RunCli("repo", "rm", fixture.RepoURL(fixture.RepoURLTypeHelm))
		assert.NoError(t, err)

		repo, err = repoClient.List(context.Background(), &repositorypkg.RepoQuery{})
		assert.NoError(t, err)
		exists = false
		for i := range repo.Items {
			if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) {
				exists = true
				break
			}
		}
		assert.False(t, exists)
	})

}

func TestAddHelmRepoInsecureSkipVerify(t *testing.T) {
	app.Given(t).And(func() {
		_, err := fixture.RunCli("repo", "add", fixture.RepoURL(fixture.RepoURLTypeHelm),
			"--name", "testrepo",
			"--type", "helm",
			"--username", fixture.GitUsername,
			"--password", fixture.GitPassword,
			"--insecure-skip-server-verification",
			"--tls-client-cert-path", repos.CertPath,
			"--tls-client-cert-key-path", repos.CertKeyPath)
		assert.NoError(t, err)

		conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
		assert.NoError(t, err)
		defer argoio.Close(conn)

		repo, err := repoClient.List(context.Background(), &repositorypkg.RepoQuery{})

		assert.NoError(t, err)
		exists := false
		for i := range repo.Items {
			if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) {
				exists = true
				break
			}
		}
		assert.True(t, exists)
	})

}
<commit_msg>fix: prevent possible null pointer dereference error in TestAddHelmRepoInsecureSkipVerify test (#3650)<commit_after>package e2e

import (
	"context"
	"testing"

	argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
	"github.com/stretchr/testify/assert"

	repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
	"github.com/argoproj/argo-cd/test/e2e/fixture"
	"github.com/argoproj/argo-cd/test/e2e/fixture/app"
	"github.com/argoproj/argo-cd/test/e2e/fixture/repos"
	"github.com/argoproj/argo-cd/util/settings"
)

func TestAddRemovePublicRepo(t *testing.T) {
	app.Given(t).And(func() {
		repoUrl := fixture.RepoURL(fixture.RepoURLTypeFile)
		_, err := fixture.RunCli("repo", "add", repoUrl)
		assert.NoError(t, err)

		conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
		assert.NoError(t, err)
		defer argoio.Close(conn)

		repo, err := repoClient.List(context.Background(), &repositorypkg.RepoQuery{})

		assert.Nil(t, err)
		exists := false
		for i := range repo.Items {
			if repo.Items[i].Repo == repoUrl {
				exists = true
				break
			}
		}
		assert.True(t, exists)

		_, err = fixture.RunCli("repo", "rm", repoUrl)
		assert.NoError(t, err)

		repo, err = repoClient.List(context.Background(), &repositorypkg.RepoQuery{})
		assert.NoError(t, err)
		exists = false
		for i := range repo.Items {
			if repo.Items[i].Repo == repoUrl {
				exists = true
				break
			}
		}
		assert.False(t, exists)
	})
}

func TestUpsertExistingRepo(t *testing.T) {
	app.Given(t).And(func() {
		fixture.SetRepos(settings.RepositoryCredentials{URL: fixture.RepoURL(fixture.RepoURLTypeFile)})
		repoUrl := fixture.RepoURL(fixture.RepoURLTypeFile)
		_, err := fixture.RunCli("repo", "add", repoUrl)
		assert.NoError(t, err)

		_, err = fixture.RunCli("repo", "add", repoUrl, "--username", fixture.GitUsername, "--password", fixture.GitPassword)
		assert.Error(t, err)

		_, err = fixture.RunCli("repo", "add", repoUrl, "--upsert", "--username", fixture.GitUsername, "--password", fixture.GitPassword)
		assert.NoError(t, err)
	})
}

func TestAddRemoveHelmRepo(t *testing.T) {
	app.Given(t).CustomCACertAdded().And(func() {
		_, err := fixture.RunCli("repo", "add", fixture.RepoURL(fixture.RepoURLTypeHelm),
			"--name", "testrepo",
			"--type", "helm",
			"--username", fixture.GitUsername,
			"--password", fixture.GitPassword,
			"--tls-client-cert-path", repos.CertPath,
			"--tls-client-cert-key-path", repos.CertKeyPath)
		assert.NoError(t, err)

		conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
		assert.NoError(t, err)
		defer argoio.Close(conn)

		repo, err := repoClient.List(context.Background(), &repositorypkg.RepoQuery{})

		assert.NoError(t, err)
		exists := false
		for i := range repo.Items {
			if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) {
				exists = true
				break
			}
		}
		assert.True(t, exists)

		_, err = fixture.RunCli("repo", "rm", fixture.RepoURL(fixture.RepoURLTypeHelm))
		assert.NoError(t, err)

		repo, err = repoClient.List(context.Background(), &repositorypkg.RepoQuery{})
		assert.NoError(t, err)
		exists = false
		for i := range repo.Items {
			if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) {
				exists = true
				break
			}
		}
		assert.False(t, exists)
	})

}

func TestAddHelmRepoInsecureSkipVerify(t *testing.T) {
	app.Given(t).And(func() {
		_, err := fixture.RunCli("repo", "add", fixture.RepoURL(fixture.RepoURLTypeHelm),
			"--name", "testrepo",
			"--type", "helm",
			"--username", fixture.GitUsername,
			"--password", fixture.GitPassword,
			"--insecure-skip-server-verification",
			"--tls-client-cert-path", repos.CertPath,
			"--tls-client-cert-key-path", repos.CertKeyPath)

		if !assert.NoError(t, err) {
			return
		}

		conn, repoClient, err := fixture.ArgoCDClientset.NewRepoClient()
		if !assert.NoError(t, err) {
			return
		}

		defer argoio.Close(conn)

		repo, err := repoClient.List(context.Background(), &repositorypkg.RepoQuery{})

		if !assert.NoError(t, err) {
			return
		}

		exists := false
		for i := range repo.Items {
			if repo.Items[i].Repo == fixture.RepoURL(fixture.RepoURLTypeHelm) {
				exists = true
				break
			}
		}
		assert.True(t, exists)
	})

}
<|endoftext|>