<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 clientcmd

import (
	"errors"
	"os"
	"path"
	"path/filepath"
	"reflect"
	"sort"

	"k8s.io/klog"

	restclient "k8s.io/client-go/rest"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

// ConfigAccess is used by subcommands and methods in this package to load and modify the appropriate config files
type ConfigAccess interface {
	// GetLoadingPrecedence returns the slice of files that should be used for loading and inspecting the config
	GetLoadingPrecedence() []string
	// GetStartingConfig returns the config that subcommands should being operating against.  It may or may not be merged depending on loading rules
	GetStartingConfig() (*clientcmdapi.Config, error)
	// GetDefaultFilename returns the name of the file you should write into (create if necessary), if you're trying to create a new stanza as opposed to updating an existing one.
	GetDefaultFilename() string
	// IsExplicitFile indicates whether or not this command is interested in exactly one file.  This implementation only ever does that  via a flag, but implementations that handle local, global, and flags may have more
	IsExplicitFile() bool
	// GetExplicitFile returns the particular file this command is operating against.  This implementation only ever has one, but implementations that handle local, global, and flags may have more
	GetExplicitFile() string
}

type PathOptions struct {
	// GlobalFile is the full path to the file to load as the global (final) option
	GlobalFile string
	// EnvVar is the env var name that points to the list of kubeconfig files to load
	EnvVar string
	// ExplicitFileFlag is the name of the flag to use for prompting for the kubeconfig file
	ExplicitFileFlag string

	// GlobalFileSubpath is an optional value used for displaying help
	GlobalFileSubpath string

	LoadingRules *ClientConfigLoadingRules
}

func (o *PathOptions) GetEnvVarFiles() []string {
	if len(o.EnvVar) == 0 {
		return []string{}
	}

	envVarValue := os.Getenv(o.EnvVar)
	if len(envVarValue) == 0 {
		return []string{}
	}

	fileList := filepath.SplitList(envVarValue)
	// prevent the same path load multiple times
	return deduplicate(fileList)
}

func (o *PathOptions) GetLoadingPrecedence() []string {
	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
		return envVarFiles
	}

	return []string{o.GlobalFile}
}

func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) {
	// don't mutate the original
	loadingRules := *o.LoadingRules
	loadingRules.Precedence = o.GetLoadingPrecedence()

	clientConfig := NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &ConfigOverrides{})
	rawConfig, err := clientConfig.RawConfig()
	if os.IsNotExist(err) {
		return clientcmdapi.NewConfig(), nil
	}
	if err != nil {
		return nil, err
	}

	return &rawConfig, nil
}

func (o *PathOptions) GetDefaultFilename() string {
	if o.IsExplicitFile() {
		return o.GetExplicitFile()
	}

	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
		if len(envVarFiles) == 1 {
			return envVarFiles[0]
		}

		// if any of the envvar files already exists, return it
		for _, envVarFile := range envVarFiles {
			if _, err := os.Stat(envVarFile); err == nil {
				return envVarFile
			}
		}

		// otherwise, return the last one in the list
		return envVarFiles[len(envVarFiles)-1]
	}

	return o.GlobalFile
}

func (o *PathOptions) IsExplicitFile() bool {
	if len(o.LoadingRules.ExplicitPath) > 0 {
		return true
	}

	return false
}

func (o *PathOptions) GetExplicitFile() string {
	return o.LoadingRules.ExplicitPath
}

func NewDefaultPathOptions() *PathOptions {
	ret := &PathOptions{
		GlobalFile:       RecommendedHomeFile,
		EnvVar:           RecommendedConfigPathEnvVar,
		ExplicitFileFlag: RecommendedConfigPathFlag,

		GlobalFileSubpath: path.Join(RecommendedHomeDir, RecommendedFileName),

		LoadingRules: NewDefaultClientConfigLoadingRules(),
	}
	ret.LoadingRules.DoNotResolvePaths = true

	return ret
}

// ModifyConfig takes a Config object, iterates through Clusters, AuthInfos, and Contexts, uses the LocationOfOrigin if specified or
// uses the default destination file to write the results into.  This results in multiple file reads, but it's very easy to follow.
// Preferences and CurrentContext should always be set in the default destination file.  Since we can't distinguish between empty and missing values
// (no nil strings), we're forced have separate handling for them.  In the kubeconfig cases, newConfig should have at most one difference,
// that means that this code will only write into a single file.  If you want to relativizePaths, you must provide a fully qualified path in any
// modified element.
func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error {
	possibleSources := configAccess.GetLoadingPrecedence()
	// sort the possible kubeconfig files so we always "lock" in the same order
	// to avoid deadlock (note: this can fail w/ symlinks, but... come on).
	sort.Strings(possibleSources)
	for _, filename := range possibleSources {
		if err := lockFile(filename); err != nil {
			return err
		}
		defer unlockFile(filename)
	}

	startingConfig, err := configAccess.GetStartingConfig()
	if err != nil {
		return err
	}

	// We need to find all differences, locate their original files, read a partial config to modify only that stanza and write out the file.
	// Special case the test for current context and preferences since those always write to the default file.
	if reflect.DeepEqual(*startingConfig, newConfig) {
		// nothing to do
		return nil
	}

	if startingConfig.CurrentContext != newConfig.CurrentContext {
		if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil {
			return err
		}
	}

	if !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences) {
		if err := writePreferences(configAccess, newConfig.Preferences); err != nil {
			return err
		}
	}

	// Search every cluster, authInfo, and context.  First from new to old for differences, then from old to new for deletions
	for key, cluster := range newConfig.Clusters {
		startingCluster, exists := startingConfig.Clusters[key]
		if !reflect.DeepEqual(cluster, startingCluster) || !exists {
			destinationFile := cluster.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			t := *cluster

			configToWrite.Clusters[key] = &t
			configToWrite.Clusters[key].LocationOfOrigin = destinationFile
			if relativizePaths {
				if err := RelativizeClusterLocalPaths(configToWrite.Clusters[key]); err != nil {
					return err
				}
			}

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	// seenConfigs stores a map of config source filenames to computed config objects
	seenConfigs := map[string]*clientcmdapi.Config{}

	for key, context := range newConfig.Contexts {
		startingContext, exists := startingConfig.Contexts[key]
		if !reflect.DeepEqual(context, startingContext) || !exists {
			destinationFile := context.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			// we only obtain a fresh config object from its source file
			// if we have not seen it already - this prevents us from
			// reading and writing to the same number of files repeatedly
			// when multiple / all contexts share the same destination file.
			configToWrite, seen := seenConfigs[destinationFile]
			if !seen {
				var err error
				configToWrite, err = getConfigFromFile(destinationFile)
				if err != nil {
					return err
				}
				seenConfigs[destinationFile] = configToWrite
			}

			configToWrite.Contexts[key] = context
		}
	}

	// actually persist config object changes
	for destinationFile, configToWrite := range seenConfigs {
		if err := WriteToFile(*configToWrite, destinationFile); err != nil {
			return err
		}
	}

	for key, authInfo := range newConfig.AuthInfos {
		startingAuthInfo, exists := startingConfig.AuthInfos[key]
		if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists {
			destinationFile := authInfo.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			t := *authInfo
			configToWrite.AuthInfos[key] = &t
			configToWrite.AuthInfos[key].LocationOfOrigin = destinationFile
			if relativizePaths {
				if err := RelativizeAuthInfoLocalPaths(configToWrite.AuthInfos[key]); err != nil {
					return err
				}
			}

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, cluster := range startingConfig.Clusters {
		if _, exists := newConfig.Clusters[key]; !exists {
			destinationFile := cluster.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			delete(configToWrite.Clusters, key)

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, context := range startingConfig.Contexts {
		if _, exists := newConfig.Contexts[key]; !exists {
			destinationFile := context.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			delete(configToWrite.Contexts, key)

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, authInfo := range startingConfig.AuthInfos {
		if _, exists := newConfig.AuthInfos[key]; !exists {
			destinationFile := authInfo.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			delete(configToWrite.AuthInfos, key)

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	return nil
}

func PersisterForUser(configAccess ConfigAccess, user string) restclient.AuthProviderConfigPersister {
	return &persister{configAccess, user}
}

type persister struct {
	configAccess ConfigAccess
	user         string
}

func (p *persister) Persist(config map[string]string) error {
	newConfig, err := p.configAccess.GetStartingConfig()
	if err != nil {
		return err
	}
	authInfo, ok := newConfig.AuthInfos[p.user]
	if ok && authInfo.AuthProvider != nil {
		authInfo.AuthProvider.Config = config
		ModifyConfig(p.configAccess, *newConfig, false)
	}
	return nil
}

// writeCurrentContext takes three possible paths.
// If newCurrentContext is the same as the startingConfig's current context, then we exit.
// If newCurrentContext has a value, then that value is written into the default destination file.
// If newCurrentContext is empty, then we find the config file that is setting the CurrentContext and clear the value from that file
func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error {
	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
		return err
	} else if startingConfig.CurrentContext == newCurrentContext {
		return nil
	}

	if configAccess.IsExplicitFile() {
		file := configAccess.GetExplicitFile()
		currConfig, err := getConfigFromFile(file)
		if err != nil {
			return err
		}
		currConfig.CurrentContext = newCurrentContext
		if err := WriteToFile(*currConfig, file); err != nil {
			return err
		}

		return nil
	}

	if len(newCurrentContext) > 0 {
		destinationFile := configAccess.GetDefaultFilename()
		config, err := getConfigFromFile(destinationFile)
		if err != nil {
			return err
		}
		config.CurrentContext = newCurrentContext

		if err := WriteToFile(*config, destinationFile); err != nil {
			return err
		}

		return nil
	}

	// we're supposed to be clearing the current context.  We need to find the first spot in the chain that is setting it and clear it
	for _, file := range configAccess.GetLoadingPrecedence() {
		if _, err := os.Stat(file); err == nil {
			currConfig, err := getConfigFromFile(file)
			if err != nil {
				return err
			}

			if len(currConfig.CurrentContext) > 0 {
				currConfig.CurrentContext = newCurrentContext
				if err := WriteToFile(*currConfig, file); err != nil {
					return err
				}

				return nil
			}
		}
	}

	return errors.New("no config found to write context")
}

func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error {
	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
		return err
	} else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) {
		return nil
	}

	if configAccess.IsExplicitFile() {
		file := configAccess.GetExplicitFile()
		currConfig, err := getConfigFromFile(file)
		if err != nil {
			return err
		}
		currConfig.Preferences = newPrefs
		if err := WriteToFile(*currConfig, file); err != nil {
			return err
		}

		return nil
	}

	for _, file := range configAccess.GetLoadingPrecedence() {
		currConfig, err := getConfigFromFile(file)
		if err != nil {
			return err
		}

		if !reflect.DeepEqual(currConfig.Preferences, newPrefs) {
			currConfig.Preferences = newPrefs
			if err := WriteToFile(*currConfig, file); err != nil {
				return err
			}

			return nil
		}
	}

	return errors.New("no config found to write preferences")
}

// getConfigFromFile tries to read a kubeconfig file and if it can't, returns an error.  One exception, missing files result in empty configs, not an error.
func getConfigFromFile(filename string) (*clientcmdapi.Config, error) {
	config, err := LoadFromFile(filename)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	if config == nil {
		config = clientcmdapi.NewConfig()
	}
	return config, nil
}

// GetConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit.  One exception, missing files result in empty configs, not an exit
func GetConfigFromFileOrDie(filename string) *clientcmdapi.Config {
	config, err := getConfigFromFile(filename)
	if err != nil {
		klog.FatalDepth(1, err)
	}

	return config
}
<commit_msg>UPSTREAM: <carry>: disable file locking for kubeconfig because it breaks tests<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 clientcmd

import (
	"errors"
	"os"
	"path"
	"path/filepath"
	"reflect"

	"k8s.io/klog"

	restclient "k8s.io/client-go/rest"
	clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
)

// ConfigAccess is used by subcommands and methods in this package to load and modify the appropriate config files
type ConfigAccess interface {
	// GetLoadingPrecedence returns the slice of files that should be used for loading and inspecting the config
	GetLoadingPrecedence() []string
	// GetStartingConfig returns the config that subcommands should being operating against.  It may or may not be merged depending on loading rules
	GetStartingConfig() (*clientcmdapi.Config, error)
	// GetDefaultFilename returns the name of the file you should write into (create if necessary), if you're trying to create a new stanza as opposed to updating an existing one.
	GetDefaultFilename() string
	// IsExplicitFile indicates whether or not this command is interested in exactly one file.  This implementation only ever does that  via a flag, but implementations that handle local, global, and flags may have more
	IsExplicitFile() bool
	// GetExplicitFile returns the particular file this command is operating against.  This implementation only ever has one, but implementations that handle local, global, and flags may have more
	GetExplicitFile() string
}

type PathOptions struct {
	// GlobalFile is the full path to the file to load as the global (final) option
	GlobalFile string
	// EnvVar is the env var name that points to the list of kubeconfig files to load
	EnvVar string
	// ExplicitFileFlag is the name of the flag to use for prompting for the kubeconfig file
	ExplicitFileFlag string

	// GlobalFileSubpath is an optional value used for displaying help
	GlobalFileSubpath string

	LoadingRules *ClientConfigLoadingRules
}

func (o *PathOptions) GetEnvVarFiles() []string {
	if len(o.EnvVar) == 0 {
		return []string{}
	}

	envVarValue := os.Getenv(o.EnvVar)
	if len(envVarValue) == 0 {
		return []string{}
	}

	fileList := filepath.SplitList(envVarValue)
	// prevent the same path load multiple times
	return deduplicate(fileList)
}

func (o *PathOptions) GetLoadingPrecedence() []string {
	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
		return envVarFiles
	}

	return []string{o.GlobalFile}
}

func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) {
	// don't mutate the original
	loadingRules := *o.LoadingRules
	loadingRules.Precedence = o.GetLoadingPrecedence()

	clientConfig := NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &ConfigOverrides{})
	rawConfig, err := clientConfig.RawConfig()
	if os.IsNotExist(err) {
		return clientcmdapi.NewConfig(), nil
	}
	if err != nil {
		return nil, err
	}

	return &rawConfig, nil
}

func (o *PathOptions) GetDefaultFilename() string {
	if o.IsExplicitFile() {
		return o.GetExplicitFile()
	}

	if envVarFiles := o.GetEnvVarFiles(); len(envVarFiles) > 0 {
		if len(envVarFiles) == 1 {
			return envVarFiles[0]
		}

		// if any of the envvar files already exists, return it
		for _, envVarFile := range envVarFiles {
			if _, err := os.Stat(envVarFile); err == nil {
				return envVarFile
			}
		}

		// otherwise, return the last one in the list
		return envVarFiles[len(envVarFiles)-1]
	}

	return o.GlobalFile
}

func (o *PathOptions) IsExplicitFile() bool {
	if len(o.LoadingRules.ExplicitPath) > 0 {
		return true
	}

	return false
}

func (o *PathOptions) GetExplicitFile() string {
	return o.LoadingRules.ExplicitPath
}

func NewDefaultPathOptions() *PathOptions {
	ret := &PathOptions{
		GlobalFile:       RecommendedHomeFile,
		EnvVar:           RecommendedConfigPathEnvVar,
		ExplicitFileFlag: RecommendedConfigPathFlag,

		GlobalFileSubpath: path.Join(RecommendedHomeDir, RecommendedFileName),

		LoadingRules: NewDefaultClientConfigLoadingRules(),
	}
	ret.LoadingRules.DoNotResolvePaths = true

	return ret
}

// ModifyConfig takes a Config object, iterates through Clusters, AuthInfos, and Contexts, uses the LocationOfOrigin if specified or
// uses the default destination file to write the results into.  This results in multiple file reads, but it's very easy to follow.
// Preferences and CurrentContext should always be set in the default destination file.  Since we can't distinguish between empty and missing values
// (no nil strings), we're forced have separate handling for them.  In the kubeconfig cases, newConfig should have at most one difference,
// that means that this code will only write into a single file.  If you want to relativizePaths, you must provide a fully qualified path in any
// modified element.
func ModifyConfig(configAccess ConfigAccess, newConfig clientcmdapi.Config, relativizePaths bool) error {
	startingConfig, err := configAccess.GetStartingConfig()
	if err != nil {
		return err
	}

	// We need to find all differences, locate their original files, read a partial config to modify only that stanza and write out the file.
	// Special case the test for current context and preferences since those always write to the default file.
	if reflect.DeepEqual(*startingConfig, newConfig) {
		// nothing to do
		return nil
	}

	if startingConfig.CurrentContext != newConfig.CurrentContext {
		if err := writeCurrentContext(configAccess, newConfig.CurrentContext); err != nil {
			return err
		}
	}

	if !reflect.DeepEqual(startingConfig.Preferences, newConfig.Preferences) {
		if err := writePreferences(configAccess, newConfig.Preferences); err != nil {
			return err
		}
	}

	// Search every cluster, authInfo, and context.  First from new to old for differences, then from old to new for deletions
	for key, cluster := range newConfig.Clusters {
		startingCluster, exists := startingConfig.Clusters[key]
		if !reflect.DeepEqual(cluster, startingCluster) || !exists {
			destinationFile := cluster.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			t := *cluster

			configToWrite.Clusters[key] = &t
			configToWrite.Clusters[key].LocationOfOrigin = destinationFile
			if relativizePaths {
				if err := RelativizeClusterLocalPaths(configToWrite.Clusters[key]); err != nil {
					return err
				}
			}

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	// seenConfigs stores a map of config source filenames to computed config objects
	seenConfigs := map[string]*clientcmdapi.Config{}

	for key, context := range newConfig.Contexts {
		startingContext, exists := startingConfig.Contexts[key]
		if !reflect.DeepEqual(context, startingContext) || !exists {
			destinationFile := context.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			// we only obtain a fresh config object from its source file
			// if we have not seen it already - this prevents us from
			// reading and writing to the same number of files repeatedly
			// when multiple / all contexts share the same destination file.
			configToWrite, seen := seenConfigs[destinationFile]
			if !seen {
				var err error
				configToWrite, err = getConfigFromFile(destinationFile)
				if err != nil {
					return err
				}
				seenConfigs[destinationFile] = configToWrite
			}

			configToWrite.Contexts[key] = context
		}
	}

	// actually persist config object changes
	for destinationFile, configToWrite := range seenConfigs {
		if err := WriteToFile(*configToWrite, destinationFile); err != nil {
			return err
		}
	}

	for key, authInfo := range newConfig.AuthInfos {
		startingAuthInfo, exists := startingConfig.AuthInfos[key]
		if !reflect.DeepEqual(authInfo, startingAuthInfo) || !exists {
			destinationFile := authInfo.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			t := *authInfo
			configToWrite.AuthInfos[key] = &t
			configToWrite.AuthInfos[key].LocationOfOrigin = destinationFile
			if relativizePaths {
				if err := RelativizeAuthInfoLocalPaths(configToWrite.AuthInfos[key]); err != nil {
					return err
				}
			}

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, cluster := range startingConfig.Clusters {
		if _, exists := newConfig.Clusters[key]; !exists {
			destinationFile := cluster.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			delete(configToWrite.Clusters, key)

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, context := range startingConfig.Contexts {
		if _, exists := newConfig.Contexts[key]; !exists {
			destinationFile := context.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			delete(configToWrite.Contexts, key)

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	for key, authInfo := range startingConfig.AuthInfos {
		if _, exists := newConfig.AuthInfos[key]; !exists {
			destinationFile := authInfo.LocationOfOrigin
			if len(destinationFile) == 0 {
				destinationFile = configAccess.GetDefaultFilename()
			}

			configToWrite, err := getConfigFromFile(destinationFile)
			if err != nil {
				return err
			}
			delete(configToWrite.AuthInfos, key)

			if err := WriteToFile(*configToWrite, destinationFile); err != nil {
				return err
			}
		}
	}

	return nil
}

func PersisterForUser(configAccess ConfigAccess, user string) restclient.AuthProviderConfigPersister {
	return &persister{configAccess, user}
}

type persister struct {
	configAccess ConfigAccess
	user         string
}

func (p *persister) Persist(config map[string]string) error {
	newConfig, err := p.configAccess.GetStartingConfig()
	if err != nil {
		return err
	}
	authInfo, ok := newConfig.AuthInfos[p.user]
	if ok && authInfo.AuthProvider != nil {
		authInfo.AuthProvider.Config = config
		ModifyConfig(p.configAccess, *newConfig, false)
	}
	return nil
}

// writeCurrentContext takes three possible paths.
// If newCurrentContext is the same as the startingConfig's current context, then we exit.
// If newCurrentContext has a value, then that value is written into the default destination file.
// If newCurrentContext is empty, then we find the config file that is setting the CurrentContext and clear the value from that file
func writeCurrentContext(configAccess ConfigAccess, newCurrentContext string) error {
	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
		return err
	} else if startingConfig.CurrentContext == newCurrentContext {
		return nil
	}

	if configAccess.IsExplicitFile() {
		file := configAccess.GetExplicitFile()
		currConfig, err := getConfigFromFile(file)
		if err != nil {
			return err
		}
		currConfig.CurrentContext = newCurrentContext
		if err := WriteToFile(*currConfig, file); err != nil {
			return err
		}

		return nil
	}

	if len(newCurrentContext) > 0 {
		destinationFile := configAccess.GetDefaultFilename()
		config, err := getConfigFromFile(destinationFile)
		if err != nil {
			return err
		}
		config.CurrentContext = newCurrentContext

		if err := WriteToFile(*config, destinationFile); err != nil {
			return err
		}

		return nil
	}

	// we're supposed to be clearing the current context.  We need to find the first spot in the chain that is setting it and clear it
	for _, file := range configAccess.GetLoadingPrecedence() {
		if _, err := os.Stat(file); err == nil {
			currConfig, err := getConfigFromFile(file)
			if err != nil {
				return err
			}

			if len(currConfig.CurrentContext) > 0 {
				currConfig.CurrentContext = newCurrentContext
				if err := WriteToFile(*currConfig, file); err != nil {
					return err
				}

				return nil
			}
		}
	}

	return errors.New("no config found to write context")
}

func writePreferences(configAccess ConfigAccess, newPrefs clientcmdapi.Preferences) error {
	if startingConfig, err := configAccess.GetStartingConfig(); err != nil {
		return err
	} else if reflect.DeepEqual(startingConfig.Preferences, newPrefs) {
		return nil
	}

	if configAccess.IsExplicitFile() {
		file := configAccess.GetExplicitFile()
		currConfig, err := getConfigFromFile(file)
		if err != nil {
			return err
		}
		currConfig.Preferences = newPrefs
		if err := WriteToFile(*currConfig, file); err != nil {
			return err
		}

		return nil
	}

	for _, file := range configAccess.GetLoadingPrecedence() {
		currConfig, err := getConfigFromFile(file)
		if err != nil {
			return err
		}

		if !reflect.DeepEqual(currConfig.Preferences, newPrefs) {
			currConfig.Preferences = newPrefs
			if err := WriteToFile(*currConfig, file); err != nil {
				return err
			}

			return nil
		}
	}

	return errors.New("no config found to write preferences")
}

// getConfigFromFile tries to read a kubeconfig file and if it can't, returns an error.  One exception, missing files result in empty configs, not an error.
func getConfigFromFile(filename string) (*clientcmdapi.Config, error) {
	config, err := LoadFromFile(filename)
	if err != nil && !os.IsNotExist(err) {
		return nil, err
	}
	if config == nil {
		config = clientcmdapi.NewConfig()
	}
	return config, nil
}

// GetConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit.  One exception, missing files result in empty configs, not an exit
func GetConfigFromFileOrDie(filename string) *clientcmdapi.Config {
	config, err := getConfigFromFile(filename)
	if err != nil {
		klog.FatalDepth(1, err)
	}

	return config
}
<|endoftext|>


<commit_before>package engine

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"
)

// A job is the fundamental unit of work in the docker engine.
// Everything docker can do should eventually be exposed as a job.
// For example: execute a process in a container, create a new container,
// download an archive from the internet, serve the http api, etc.
//
// The job API is designed after unix processes: a job has a name, arguments,
// environment variables, standard streams for input, output and error, and
// an exit status which can indicate success (0) or error (anything else).
//
// One slight variation is that jobs report their status as a string. The
// string "0" indicates success, and any other strings indicates an error.
// This allows for richer error reporting.
//
type Job struct {
	Eng     *Engine
	Name    string
	Args    []string
	env     *Env
	Stdout  *Output
	Stderr  *Output
	Stdin   *Input
	handler Handler
	status  Status
	end     time.Time
	onExit  []func()
}

type Status int

const (
	StatusOK       Status = 0
	StatusErr      Status = 1
	StatusNotFound Status = 127
)

// Run executes the job and blocks until the job completes.
// If the job returns a failure status, an error is returned
// which includes the status.
func (job *Job) Run() error {
	if job.Eng.IsShutdown() {
		return fmt.Errorf("engine is shutdown")
	}
	job.Eng.l.Lock()
	job.Eng.tasks.Add(1)
	job.Eng.l.Unlock()
	defer job.Eng.tasks.Done()
	// FIXME: make this thread-safe
	// FIXME: implement wait
	if !job.end.IsZero() {
		return fmt.Errorf("%s: job has already completed", job.Name)
	}
	// Log beginning and end of the job
	job.Eng.Logf("+job %s", job.CallString())
	defer func() {
		job.Eng.Logf("-job %s%s", job.CallString(), job.StatusString())
	}()
	var errorMessage = bytes.NewBuffer(nil)
	job.Stderr.Add(errorMessage)
	if job.handler == nil {
		job.Errorf("%s: command not found", job.Name)
		job.status = 127
	} else {
		job.status = job.handler(job)
		job.end = time.Now()
	}
	// Wait for all background tasks to complete
	if err := job.Stdout.Close(); err != nil {
		return err
	}
	if err := job.Stderr.Close(); err != nil {
		return err
	}
	if err := job.Stdin.Close(); err != nil {
		return err
	}
	if job.status != 0 {
		return fmt.Errorf("%s", Tail(errorMessage, 1))
	}
	return nil
}

func (job *Job) CallString() string {
	return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", "))
}

func (job *Job) StatusString() string {
	// If the job hasn't completed, status string is empty
	if job.end.IsZero() {
		return ""
	}
	var okerr string
	if job.status == StatusOK {
		okerr = "OK"
	} else {
		okerr = "ERR"
	}
	return fmt.Sprintf(" = %s (%d)", okerr, job.status)
}

// String returns a human-readable description of `job`
func (job *Job) String() string {
	return fmt.Sprintf("%s.%s%s", job.Eng, job.CallString(), job.StatusString())
}

func (job *Job) Env() *Env {
	return job.env
}

func (job *Job) EnvExists(key string) (value bool) {
	return job.env.Exists(key)
}

func (job *Job) Getenv(key string) (value string) {
	return job.env.Get(key)
}

func (job *Job) GetenvBool(key string) (value bool) {
	return job.env.GetBool(key)
}

func (job *Job) SetenvBool(key string, value bool) {
	job.env.SetBool(key, value)
}

func (job *Job) GetenvSubEnv(key string) *Env {
	return job.env.GetSubEnv(key)
}

func (job *Job) SetenvSubEnv(key string, value *Env) error {
	return job.env.SetSubEnv(key, value)
}

func (job *Job) GetenvInt64(key string) int64 {
	return job.env.GetInt64(key)
}

func (job *Job) GetenvInt(key string) int {
	return job.env.GetInt(key)
}

func (job *Job) SetenvInt64(key string, value int64) {
	job.env.SetInt64(key, value)
}

func (job *Job) SetenvInt(key string, value int) {
	job.env.SetInt(key, value)
}

// Returns nil if key not found
func (job *Job) GetenvList(key string) []string {
	return job.env.GetList(key)
}

func (job *Job) GetenvJson(key string, iface interface{}) error {
	return job.env.GetJson(key, iface)
}

func (job *Job) SetenvJson(key string, value interface{}) error {
	return job.env.SetJson(key, value)
}

func (job *Job) SetenvList(key string, value []string) error {
	return job.env.SetJson(key, value)
}

func (job *Job) Setenv(key, value string) {
	job.env.Set(key, value)
}

// DecodeEnv decodes `src` as a json dictionary, and adds
// each decoded key-value pair to the environment.
//
// If `src` cannot be decoded as a json dictionary, an error
// is returned.
func (job *Job) DecodeEnv(src io.Reader) error {
	return job.env.Decode(src)
}

func (job *Job) EncodeEnv(dst io.Writer) error {
	return job.env.Encode(dst)
}

func (job *Job) ImportEnv(src interface{}) (err error) {
	return job.env.Import(src)
}

func (job *Job) Environ() map[string]string {
	return job.env.Map()
}

func (job *Job) Logf(format string, args ...interface{}) (n int, err error) {
	prefixedFormat := fmt.Sprintf("[%s] %s\n", job, strings.TrimRight(format, "\n"))
	return fmt.Fprintf(job.Stderr, prefixedFormat, args...)
}

func (job *Job) Printf(format string, args ...interface{}) (n int, err error) {
	return fmt.Fprintf(job.Stdout, format, args...)
}

func (job *Job) Errorf(format string, args ...interface{}) Status {
	if format[len(format)-1] != '\n' {
		format = format + "\n"
	}
	fmt.Fprintf(job.Stderr, format, args...)
	return StatusErr
}

func (job *Job) Error(err error) Status {
	fmt.Fprintf(job.Stderr, "%s\n", err)
	return StatusErr
}

func (job *Job) StatusCode() int {
	return int(job.status)
}
<commit_msg>Remove unused field from engine.Job<commit_after>package engine

import (
	"bytes"
	"fmt"
	"io"
	"strings"
	"time"
)

// A job is the fundamental unit of work in the docker engine.
// Everything docker can do should eventually be exposed as a job.
// For example: execute a process in a container, create a new container,
// download an archive from the internet, serve the http api, etc.
//
// The job API is designed after unix processes: a job has a name, arguments,
// environment variables, standard streams for input, output and error, and
// an exit status which can indicate success (0) or error (anything else).
//
// One slight variation is that jobs report their status as a string. The
// string "0" indicates success, and any other strings indicates an error.
// This allows for richer error reporting.
//
type Job struct {
	Eng     *Engine
	Name    string
	Args    []string
	env     *Env
	Stdout  *Output
	Stderr  *Output
	Stdin   *Input
	handler Handler
	status  Status
	end     time.Time
}

type Status int

const (
	StatusOK       Status = 0
	StatusErr      Status = 1
	StatusNotFound Status = 127
)

// Run executes the job and blocks until the job completes.
// If the job returns a failure status, an error is returned
// which includes the status.
func (job *Job) Run() error {
	if job.Eng.IsShutdown() {
		return fmt.Errorf("engine is shutdown")
	}
	job.Eng.l.Lock()
	job.Eng.tasks.Add(1)
	job.Eng.l.Unlock()
	defer job.Eng.tasks.Done()
	// FIXME: make this thread-safe
	// FIXME: implement wait
	if !job.end.IsZero() {
		return fmt.Errorf("%s: job has already completed", job.Name)
	}
	// Log beginning and end of the job
	job.Eng.Logf("+job %s", job.CallString())
	defer func() {
		job.Eng.Logf("-job %s%s", job.CallString(), job.StatusString())
	}()
	var errorMessage = bytes.NewBuffer(nil)
	job.Stderr.Add(errorMessage)
	if job.handler == nil {
		job.Errorf("%s: command not found", job.Name)
		job.status = 127
	} else {
		job.status = job.handler(job)
		job.end = time.Now()
	}
	// Wait for all background tasks to complete
	if err := job.Stdout.Close(); err != nil {
		return err
	}
	if err := job.Stderr.Close(); err != nil {
		return err
	}
	if err := job.Stdin.Close(); err != nil {
		return err
	}
	if job.status != 0 {
		return fmt.Errorf("%s", Tail(errorMessage, 1))
	}
	return nil
}

func (job *Job) CallString() string {
	return fmt.Sprintf("%s(%s)", job.Name, strings.Join(job.Args, ", "))
}

func (job *Job) StatusString() string {
	// If the job hasn't completed, status string is empty
	if job.end.IsZero() {
		return ""
	}
	var okerr string
	if job.status == StatusOK {
		okerr = "OK"
	} else {
		okerr = "ERR"
	}
	return fmt.Sprintf(" = %s (%d)", okerr, job.status)
}

// String returns a human-readable description of `job`
func (job *Job) String() string {
	return fmt.Sprintf("%s.%s%s", job.Eng, job.CallString(), job.StatusString())
}

func (job *Job) Env() *Env {
	return job.env
}

func (job *Job) EnvExists(key string) (value bool) {
	return job.env.Exists(key)
}

func (job *Job) Getenv(key string) (value string) {
	return job.env.Get(key)
}

func (job *Job) GetenvBool(key string) (value bool) {
	return job.env.GetBool(key)
}

func (job *Job) SetenvBool(key string, value bool) {
	job.env.SetBool(key, value)
}

func (job *Job) GetenvSubEnv(key string) *Env {
	return job.env.GetSubEnv(key)
}

func (job *Job) SetenvSubEnv(key string, value *Env) error {
	return job.env.SetSubEnv(key, value)
}

func (job *Job) GetenvInt64(key string) int64 {
	return job.env.GetInt64(key)
}

func (job *Job) GetenvInt(key string) int {
	return job.env.GetInt(key)
}

func (job *Job) SetenvInt64(key string, value int64) {
	job.env.SetInt64(key, value)
}

func (job *Job) SetenvInt(key string, value int) {
	job.env.SetInt(key, value)
}

// Returns nil if key not found
func (job *Job) GetenvList(key string) []string {
	return job.env.GetList(key)
}

func (job *Job) GetenvJson(key string, iface interface{}) error {
	return job.env.GetJson(key, iface)
}

func (job *Job) SetenvJson(key string, value interface{}) error {
	return job.env.SetJson(key, value)
}

func (job *Job) SetenvList(key string, value []string) error {
	return job.env.SetJson(key, value)
}

func (job *Job) Setenv(key, value string) {
	job.env.Set(key, value)
}

// DecodeEnv decodes `src` as a json dictionary, and adds
// each decoded key-value pair to the environment.
//
// If `src` cannot be decoded as a json dictionary, an error
// is returned.
func (job *Job) DecodeEnv(src io.Reader) error {
	return job.env.Decode(src)
}

func (job *Job) EncodeEnv(dst io.Writer) error {
	return job.env.Encode(dst)
}

func (job *Job) ImportEnv(src interface{}) (err error) {
	return job.env.Import(src)
}

func (job *Job) Environ() map[string]string {
	return job.env.Map()
}

func (job *Job) Logf(format string, args ...interface{}) (n int, err error) {
	prefixedFormat := fmt.Sprintf("[%s] %s\n", job, strings.TrimRight(format, "\n"))
	return fmt.Fprintf(job.Stderr, prefixedFormat, args...)
}

func (job *Job) Printf(format string, args ...interface{}) (n int, err error) {
	return fmt.Fprintf(job.Stdout, format, args...)
}

func (job *Job) Errorf(format string, args ...interface{}) Status {
	if format[len(format)-1] != '\n' {
		format = format + "\n"
	}
	fmt.Fprintf(job.Stderr, format, args...)
	return StatusErr
}

func (job *Job) Error(err error) Status {
	fmt.Fprintf(job.Stderr, "%s\n", err)
	return StatusErr
}

func (job *Job) StatusCode() int {
	return int(job.status)
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Tekton 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 test

import (
	"os"
	"regexp"
	"runtime"
	"testing"

	"k8s.io/apimachinery/pkg/util/sets"
)

var (
	imageNames      = initImageNames()
	excludedTests   = initExcludedTests()
	imagesMappingRE map[*regexp.Regexp][]byte
)

const (
	// Busybox image with specific sha
	busyboxImage = iota
	// Registry image
	registryImage
	//kubectl image
	kubectlImage
	//helm image
	helmImage
	//kaniko executor image
	kanikoImage
	// dockerize image
	dockerizeImage
)

func init() {
	imagesMappingRE = getImagesMappingRE()
}

// getTestArch returns architecture of the cluster where test suites will be executed.
// default value is similar to build architecture, TEST_RUNTIME_ARCH is used when test target cluster has another architecture
func getTestArch() string {
	val, ok := os.LookupEnv("TEST_RUNTIME_ARCH")
	if ok {
		return val
	}
	return runtime.GOARCH
}

// initImageNames returns the map with arch dependent image names for e2e tests
func initImageNames() map[int]string {
	if getTestArch() == "s390x" {
		return map[int]string{
			busyboxImage:   "busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977",
			registryImage:  "ibmcom/registry:2.6.2.5",
			kubectlImage:   "ibmcom/kubectl:v1.13.9",
			helmImage:      "ibmcom/alpine-helm-s390x:latest",
			kanikoImage:    "gcr.io/kaniko-project/executor:s390x-9ed158c1f63a059cde4fd5f8b95af51d452d9aa7",
			dockerizeImage: "ibmcom/dockerize-s390x",
		}
	}
	return map[int]string{
		busyboxImage:   "busybox@sha256:895ab622e92e18d6b461d671081757af7dbaa3b00e3e28e12505af7817f73649",
		registryImage:  "registry",
		kubectlImage:   "lachlanevenson/k8s-kubectl",
		helmImage:      "alpine/helm:3.1.2",
		kanikoImage:    "gcr.io/kaniko-project/executor:v1.3.0",
		dockerizeImage: "jwilder/dockerize",
	}
}

// getImagesMappingRE generates the map ready to search and replace image names with regexp for examples files.
// search is done using "image: <name>" pattern.
func getImagesMappingRE() map[*regexp.Regexp][]byte {
	imageNamesMapping := imageNamesMapping()
	imageMappingRE := make(map[*regexp.Regexp][]byte, len(imageNamesMapping))

	for existingImage, archSpecificImage := range imageNamesMapping {
		imageMappingRE[regexp.MustCompile("(?im)image: "+existingImage+"$")] = []byte("image: " + archSpecificImage)
	}

	return imageMappingRE
}

// imageNamesMapping provides mapping between image name in the examples yaml files and desired image name for specific arch.
// by default empty map is returned.
func imageNamesMapping() map[string]string {
	if getTestArch() == "s390x" {
		return map[string]string{
			"registry":                              getTestImage(registryImage),
			"node":                                  "node:alpine3.11",
			"lachlanevenson/k8s-kubectl":            getTestImage(kubectlImage),
			"gcr.io/cloud-builders/git":             "alpine/git:latest",
			"docker:dind":                           "ibmcom/docker-s390x:dind",
			"docker":                                "docker:18.06.3",
			"mikefarah/yq:3":                        "danielxlee/yq:2.4.0",
			"stedolan/jq":                           "ibmcom/jq-s390x:latest",
			"gcr.io/kaniko-project/executor:v1.3.0": getTestImage(kanikoImage),
		}
	}

	return make(map[string]string)
}

// initExcludedTests provides list of excluded tests for e2e and exanples tests
func initExcludedTests() sets.String {
	if getTestArch() == "s390x" {
		return sets.NewString(
			//examples
			"TestExamples/v1alpha1/taskruns/build-gcs-targz",
			"TestExamples/v1beta1/taskruns/build-gcs-targz",
			"TestExamples/v1beta1/taskruns/build-gcs-zip",
			"TestExamples/v1alpha1/taskruns/build-gcs-zip",
			"TestExamples/v1alpha1/taskruns/gcs-resource",
			"TestExamples/v1beta1/taskruns/gcs-resource",
			"TestExamples/v1beta1/pipelineruns/pipelinerun",
			"TestYamls/yamls/v1beta1/pipelineruns/pipelinerun.yaml",
		)
	}
	return sets.NewString()
}

// getTestImage gets test image based on unique id
func getTestImage(image int) string {
	return imageNames[image]
}

// skipIfExcluded checks if test name is in the excluded list and skip it
func skipIfExcluded(t *testing.T) {
	if excludedTests.Has(t.Name()) {
		t.Skipf("skip for %s architecture", getTestArch())
	}
}
<commit_msg>Adding power (ppc64le) architecture image mappings<commit_after>/*
Copyright 2019 The Tekton 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 test

import (
	"os"
	"regexp"
	"runtime"
	"testing"

	"k8s.io/apimachinery/pkg/util/sets"
)

var (
	imageNames      = initImageNames()
	excludedTests   = initExcludedTests()
	imagesMappingRE map[*regexp.Regexp][]byte
)

const (
	// Busybox image with specific sha
	busyboxImage = iota
	// Registry image
	registryImage
	//kubectl image
	kubectlImage
	//helm image
	helmImage
	//kaniko executor image
	kanikoImage
	// dockerize image
	dockerizeImage
)

func init() {
	imagesMappingRE = getImagesMappingRE()
}

// getTestArch returns architecture of the cluster where test suites will be executed.
// default value is similar to build architecture, TEST_RUNTIME_ARCH is used when test target cluster has another architecture
func getTestArch() string {
	val, ok := os.LookupEnv("TEST_RUNTIME_ARCH")
	if ok {
		return val
	}
	return runtime.GOARCH
}

// initImageNames returns the map with arch dependent image names for e2e tests
func initImageNames() map[int]string {
	switch getTestArch() {
	case "s390x":
		return map[int]string{
			busyboxImage:   "busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977",
			registryImage:  "ibmcom/registry:2.6.2.5",
			kubectlImage:   "ibmcom/kubectl:v1.13.9",
			helmImage:      "ibmcom/alpine-helm-s390x:latest",
			kanikoImage:    "gcr.io/kaniko-project/executor:s390x-9ed158c1f63a059cde4fd5f8b95af51d452d9aa7",
			dockerizeImage: "ibmcom/dockerize-s390x",
		}
	case "ppc64le":
		return map[int]string{
			busyboxImage:   "busybox@sha256:4f47c01fa91355af2865ac10fef5bf6ec9c7f42ad2321377c21e844427972977",
			registryImage:  "ppc64le/registry:2",
			kubectlImage:   "ibmcom/kubectl:v1.13.9",
			helmImage:      "ibmcom/helm-ppc64le:latest",
			kanikoImage:    "ibmcom/kaniko-project-executor-ppc64le:v0.17.1",
			dockerizeImage: "ibmcom/dockerize-ppc64le",
		}
	default:
		return map[int]string{
			busyboxImage:   "busybox@sha256:895ab622e92e18d6b461d671081757af7dbaa3b00e3e28e12505af7817f73649",
			registryImage:  "registry",
			kubectlImage:   "lachlanevenson/k8s-kubectl",
			helmImage:      "alpine/helm:3.1.2",
			kanikoImage:    "gcr.io/kaniko-project/executor:v1.3.0",
			dockerizeImage: "jwilder/dockerize",
		}
	}
}

// getImagesMappingRE generates the map ready to search and replace image names with regexp for examples files.
// search is done using "image: <name>" pattern.
func getImagesMappingRE() map[*regexp.Regexp][]byte {
	imageNamesMapping := imageNamesMapping()
	imageMappingRE := make(map[*regexp.Regexp][]byte, len(imageNamesMapping))

	for existingImage, archSpecificImage := range imageNamesMapping {
		imageMappingRE[regexp.MustCompile("(?im)image: "+existingImage+"$")] = []byte("image: " + archSpecificImage)
	}

	return imageMappingRE
}

// imageNamesMapping provides mapping between image name in the examples yaml files and desired image name for specific arch.
// by default empty map is returned.
func imageNamesMapping() map[string]string {

	switch getTestArch() {
	case "s390x":
		return map[string]string{
			"registry":                              getTestImage(registryImage),
			"node":                                  "node:alpine3.11",
			"lachlanevenson/k8s-kubectl":            getTestImage(kubectlImage),
			"gcr.io/cloud-builders/git":             "alpine/git:latest",
			"docker:dind":                           "ibmcom/docker-s390x:dind",
			"docker":                                "docker:18.06.3",
			"mikefarah/yq:3":                        "danielxlee/yq:2.4.0",
			"stedolan/jq":                           "ibmcom/jq-s390x:latest",
			"gcr.io/kaniko-project/executor:v1.3.0": getTestImage(kanikoImage),
		}
	case "ppc64le":
		return map[string]string{
			"registry":                              getTestImage(registryImage),
			"node":                                  "node:alpine3.11",
			"lachlanevenson/k8s-kubectl":            getTestImage(kubectlImage),
			"gcr.io/cloud-builders/git":             "alpine/git:latest",
			"docker:dind":                           "ibmcom/docker-ppc64le:19.03-dind",
			"docker":                                "docker:18.06.3",
			"mikefarah/yq:3":                        "danielxlee/yq:2.4.0",
			"stedolan/jq":                           "ibmcom/jq-ppc64le:latest",
			"gcr.io/kaniko-project/executor:v1.3.0": getTestImage(kanikoImage),
		}

	}

	return make(map[string]string)
}

// initExcludedTests provides list of excluded tests for e2e and exanples tests
func initExcludedTests() sets.String {

	switch getTestArch() {
	case "s390x":
		return sets.NewString(
			//examples
			"TestExamples/v1alpha1/taskruns/build-gcs-targz",
			"TestExamples/v1beta1/taskruns/build-gcs-targz",
			"TestExamples/v1beta1/taskruns/build-gcs-zip",
			"TestExamples/v1alpha1/taskruns/build-gcs-zip",
			"TestExamples/v1alpha1/taskruns/gcs-resource",
			"TestExamples/v1beta1/taskruns/gcs-resource",
			"TestExamples/v1beta1/pipelineruns/pipelinerun",
			"TestYamls/yamls/v1beta1/pipelineruns/pipelinerun.yaml",
		)
	case "ppc64le":
		return sets.NewString(
			//examples
			"TestExamples/v1alpha1/taskruns/build-gcs-targz",
			"TestExamples/v1beta1/taskruns/build-gcs-targz",
			"TestExamples/v1beta1/taskruns/build-gcs-zip",
			"TestExamples/v1alpha1/taskruns/build-gcs-zip",
			"TestExamples/v1alpha1/taskruns/gcs-resource",
			"TestExamples/v1beta1/taskruns/gcs-resource",
			"TestExamples/v1beta1/pipelineruns/pipelinerun",
			"TestYamls/yamls/v1beta1/pipelineruns/pipelinerun.yaml",
		)
	}

	return sets.NewString()
}

// getTestImage gets test image based on unique id
func getTestImage(image int) string {
	return imageNames[image]
}

// skipIfExcluded checks if test name is in the excluded list and skip it
func skipIfExcluded(t *testing.T) {
	if excludedTests.Has(t.Name()) {
		t.Skipf("skip for %s architecture", getTestArch())
	}
}
<|endoftext|>


<commit_before>package particles

func (ps *ParticleSystem) UpdateSystemSdl2() {

}
<commit_msg>Add UpdateSystemSdl2()<commit_after>package particles

// UpdateSystemSdl2 execures UpdateSdl2 on every mover/particle
func (ps *ParticleSystem) UpdateSystemSdl2() {
	ps.UpdateSystem(func(p *Particle) {
		p.mover.UpdateSdl2()
	})
}
<|endoftext|>


<commit_before>// All this package really does is add a little structure and a little color
// to the standard log package

package logsip

import (
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/iamthemuffinman/logsip/isatty"
)

type Level uint8

var startTime time.Time

func init() {
	startTime = time.Now()
}

func sinceStartTime() int {
	return int(time.Since(startTime) / time.Second)
}

func (level Level) String() string {
	switch level {
	case PanicLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Red}}PANIC[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("PANIC[%04d] ", sinceStartTime())
	case FatalLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Red}}FATAL[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("FATAL[%04d] ", sinceStartTime())
	case ErrorLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Red}}ERROR[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("ERROR[%04d] ", sinceStartTime())
	case WarnLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Yellow}}WARN[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("WARN[%04d] ", sinceStartTime())
	case InfoLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Blue}}INFO[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("INFO[%04d] ", sinceStartTime())
	case DebugLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Purple}}DEBUG[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("DEBUG[%04d] ", sinceStartTime())
	}

	return fmt.Sprintf("UNKNOWN ")
}

const (
	PanicLevel Level = iota
	FatalLevel
	ErrorLevel
	WarnLevel
	InfoLevel
	DebugLevel
)

func parseLevel(level Level) (Level, error) {
	switch level {
	case PanicLevel:
		return PanicLevel, nil
	case FatalLevel:
		return FatalLevel, nil
	case ErrorLevel:
		return ErrorLevel, nil
	case WarnLevel:
		return WarnLevel, nil
	case InfoLevel:
		return InfoLevel, nil
	case DebugLevel:
		return DebugLevel, nil
	}

	return PanicLevel, errors.New("unable to parse log level")
}

func (logger *Logger) SetLevel(level Level) {
	l, err := parseLevel(level)
	if err != nil {
		fmt.Printf("%v", err)
	}

	logger.mu.Lock()
	defer logger.mu.Unlock()
	logger.Level = l
}

func (logger *Logger) GetLevel() Level {
	logger.mu.Lock()
	defer logger.mu.Unlock()
	return logger.Level
}

func SetLevel(level Level) {
	l, err := parseLevel(level)
	if err != nil {
		fmt.Printf("%v", err)
	}

	std.mu.Lock()
	defer std.mu.Unlock()
	std.Level = l
}

func GetLevel() Level {
	std.mu.Lock()
	defer std.mu.Unlock()
	return std.Level
}

type Logging interface {
	Panic(v ...interface{})
	Fatal(v ...interface{})
	Error(v ...interface{})
	Warn(v ...interface{})
	Info(v ...interface{})
	Debug(v ...interface{})

	Panicf(format string, v ...interface{})
	Fatalf(format string, v ...interface{})
	Errorf(format string, v ...interface{})
	Warnf(format string, v ...interface{})
	Infof(format string, v ...interface{})
	Debugf(format string, v ...interface{})

	Panicln(v ...interface{})
	Fatalln(v ...interface{})
	Errorln(v ...interface{})
	Warnln(v ...interface{})
	Infoln(v ...interface{})
	Debugln(v ...interface{})
}
<commit_msg>Removed logging interface and cleaned up a little<commit_after>// All this package really does is add a little structure and a little color
// to the standard log package

package logsip

import (
	"errors"
	"fmt"
	"os"
	"time"

	"github.com/iamthemuffinman/logsip/isatty"
)

// Level is a type that represents the log level
type Level uint8

var startTime time.Time

const (
	// PanicLevel is the lowest level
	PanicLevel Level = iota
	FatalLevel
	ErrorLevel
	WarnLevel
	InfoLevel
	DebugLevel
)

func init() {
	startTime = time.Now()
}

func sinceStartTime() int {
	return int(time.Since(startTime) / time.Second)
}

func (level Level) String() string {
	switch level {
	case PanicLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Red}}PANIC[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("PANIC[%04d] ", sinceStartTime())
	case FatalLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Red}}FATAL[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("FATAL[%04d] ", sinceStartTime())
	case ErrorLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Red}}ERROR[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("ERROR[%04d] ", sinceStartTime())
	case WarnLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Yellow}}WARN[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("WARN[%04d] ", sinceStartTime())
	case InfoLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Blue}}INFO[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("INFO[%04d] ", sinceStartTime())
	case DebugLevel:
		if isatty.IsTerminal(os.Stdout.Fd()) {
			return fmt.Sprintf(prettyPrint("{{.Purple}}DEBUG[%04d]{{.Default}} "), sinceStartTime())
		}
		return fmt.Sprintf("DEBUG[%04d] ", sinceStartTime())
	}

	return fmt.Sprintf("UNKNOWN ")
}

func parseLevel(level Level) (Level, error) {
	switch level {
	case PanicLevel:
		return PanicLevel, nil
	case FatalLevel:
		return FatalLevel, nil
	case ErrorLevel:
		return ErrorLevel, nil
	case WarnLevel:
		return WarnLevel, nil
	case InfoLevel:
		return InfoLevel, nil
	case DebugLevel:
		return DebugLevel, nil
	}

	return PanicLevel, errors.New("unable to parse log level")
}

// SetLevel is a method to set the log level (i.e Info, Warn, Debug, etc.)
func (logger *Logger) SetLevel(level Level) {
	l, err := parseLevel(level)
	if err != nil {
		fmt.Printf("%v", err)
	}

	logger.mu.Lock()
	defer logger.mu.Unlock()
	logger.Level = l
}

// GetLevel is a method to get the current log level
func (logger *Logger) GetLevel() Level {
	logger.mu.Lock()
	defer logger.mu.Unlock()
	return logger.Level
}

// SetLevel is a function to set the log level (i.e Info, Warn, Debug, etc.)
func SetLevel(level Level) {
	l, err := parseLevel(level)
	if err != nil {
		fmt.Printf("%v", err)
	}

	std.mu.Lock()
	defer std.mu.Unlock()
	std.Level = l
}

// GetLevel is a function to get the current log level
func GetLevel() Level {
	std.mu.Lock()
	defer std.mu.Unlock()
	return std.Level
}
<|endoftext|>


<commit_before>package engine

import (
	"flag"
	"fmt"
	"github.com/dropbox/changes-client/client"
	"github.com/dropbox/changes-client/client/adapter"
	"github.com/dropbox/changes-client/reporter"
	"log"
	"os"
	"os/signal"
	"sync"

	_ "github.com/dropbox/changes-client/adapter/basic"
	_ "github.com/dropbox/changes-client/adapter/lxc"
)

const (
	STATUS_QUEUED      = "queued"
	STATUS_IN_PROGRESS = "in_progress"
	STATUS_FINISHED    = "finished"

	RESULT_PASSED  = "passed"
	RESULT_FAILED  = "failed"
	RESULT_ABORTED = "aborted"

	SNAPSHOT_ACTIVE = "active"
	SNAPSHOT_FAILED = "failed'"
)

var (
	selectedAdapter string
	outputSnapshot  string
)

type Engine struct {
	config    *client.Config
	clientLog *client.Log
	adapter   adapter.Adapter
}

func RunBuildPlan(config *client.Config) error {
	var err error

	currentAdapter, err := adapter.Get(selectedAdapter)
	if err != nil {
		// TODO(dcramer): handle this error
		return err
	}

	engine := &Engine{
		config:    config,
		clientLog: client.NewLog(),
		adapter:   currentAdapter,
	}

	return engine.Run()
}

func (e *Engine) Run() error {
	var err error

	r := reporter.NewReporter(e.config.Server, e.config.JobstepID, e.config.Debug)
	defer r.Shutdown()

	wg := sync.WaitGroup{}

	wg.Add(1)
	go func() {
		reportLogChunks("console", e.clientLog, r)
		wg.Done()
	}()

	r.PushJobstepStatus(STATUS_IN_PROGRESS, "")

	err = e.adapter.Init(e.config)
	if err != nil {
		log.Print(fmt.Sprintf("[adapter] %s", err.Error()))
		r.PushJobstepStatus(STATUS_FINISHED, RESULT_FAILED)
		e.clientLog.Close()
		return err
	}

	err = e.adapter.Prepare(e.clientLog)
	// its important that shutdown happens in the same context as reportLogChunks
	// that is, we need all data from reporter to be sent before calling Shutdown
	defer e.adapter.Shutdown(e.clientLog)
	if err != nil {
		log.Print(fmt.Sprintf("[adapter] %s", err.Error()))
		r.PushJobstepStatus(STATUS_FINISHED, RESULT_FAILED)
		e.clientLog.Close()
		return err
	}

	result := e.runBuildPlan(r)

	if result == RESULT_PASSED && outputSnapshot != "" {
		err = e.captureSnapshot()
		if err != nil {
			r.PushSnapshotImageStatus(outputSnapshot, SNAPSHOT_FAILED)
		} else {
			r.PushSnapshotImageStatus(outputSnapshot, SNAPSHOT_ACTIVE)
		}
	}

	r.PushJobstepStatus(STATUS_FINISHED, result)

	e.clientLog.Close()

	wg.Wait()

	return err
}

func (e *Engine) executeCommands(r *reporter.Reporter) string {
	var result string = RESULT_PASSED

	wg := sync.WaitGroup{}

	for _, cmdConfig := range e.config.Cmds {
		cmd, err := client.NewCommand(cmdConfig.ID, cmdConfig.Script)
		if err != nil {
			r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 255)
			result = RESULT_FAILED
			break
		}
		r.PushCommandStatus(cmd.ID, STATUS_IN_PROGRESS, -1)

		cmd.CaptureOutput = cmdConfig.CaptureOutput

		env := os.Environ()
		for k, v := range cmdConfig.Env {
			env = append(env, k+"="+v)
		}
		cmd.Env = env

		if len(cmdConfig.Cwd) > 0 {
			cmd.Cwd = cmdConfig.Cwd
		}

		cmdResult, err := e.adapter.Run(cmd, e.clientLog)

		if err != nil {
			r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 255)
			result = RESULT_FAILED
		} else {
			if cmdResult.Success {
				if cmd.CaptureOutput {
					r.PushCommandOutput(cmd.ID, STATUS_FINISHED, 0, cmdResult.Output)
				} else {
					r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 0)
				}
			} else {
				r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 1)
				result = RESULT_FAILED
			}
		}

		wg.Add(1)
		go func(artifacts []string) {
			e.publishArtifacts(r, artifacts)
			wg.Done()
		}(cmdConfig.Artifacts)

		if result == RESULT_FAILED {
			break
		}
	}

	wg.Wait()

	return result
}

func (e *Engine) captureSnapshot() error {
	log.Printf("[adapter] Capturing snapshot %s", outputSnapshot)
	err := e.adapter.CaptureSnapshot(outputSnapshot, e.clientLog)
	if err != nil {
		log.Printf("[adapter] Failed to capture snapshot: %s", err.Error())
		return err
	}
	return nil
}

func (e *Engine) runBuildPlan(r *reporter.Reporter) string {
	var result string

	// cancellation signal
	cancel := make(chan struct{})

	// capture ctrl+c and enforce a clean shutdown
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)
	go func() {
		shuttingDown := false
		for _ = range sigchan {
			if shuttingDown {
				log.Printf("Second interrupt received. Terminating!")
				os.Exit(1)
			}

			shuttingDown = true

			log.Printf("Interrupted! Cancelling execution and cleaning up..")
			cancel <- struct{}{}
		}
	}()

	// We need to ensure that we're able to abort the build if upstream suggests
	// that it's been cancelled.
	if !e.config.Debug {
		go func() {
			um := &UpstreamMonitor{
				Config: e.config,
			}
			err := um.WaitUntilAbort()
			if err != nil {
				cancel <- struct{}{}
			}
		}()
	}

	// actually begin executing our the build plan
	finished := make(chan struct{})
	go func() {
		result = e.executeCommands(r)
		finished <- struct{}{}
	}()

	select {
	case <-finished:
	case <-cancel:
		e.clientLog.Writeln("==> ERROR: Build was aborted by upstream")
		result = RESULT_ABORTED
	}

	return result

}

func (e *Engine) publishArtifacts(r *reporter.Reporter, artifacts []string) {
	if len(artifacts) == 0 {
		e.clientLog.Writeln("==> Skipping artifact collection")
		return
	}

	e.clientLog.Writeln(fmt.Sprintf("==> Collecting artifacts matching %s", artifacts))

	matches, err := e.adapter.CollectArtifacts(artifacts, e.clientLog)
	if err != nil {
		e.clientLog.Writeln(fmt.Sprintf("==> ERROR: " + err.Error()))
		return
	}

	e.clientLog.Writeln(fmt.Sprintf("==> Found %d matching artifacts", len(matches)))

	r.PushArtifacts(matches)
}

func reportLogChunks(name string, clientLog *client.Log, r *reporter.Reporter) {
	for chunk := range clientLog.Chan {
		r.PushLogChunk(name, chunk)
	}
}

func init() {
	flag.StringVar(&selectedAdapter, "adapter", "basic", "Adapter to run build against")
	flag.StringVar(&outputSnapshot, "save-snapshot", "", "Save the resulting container snapshot")
}
<commit_msg>language<commit_after>package engine

import (
	"flag"
	"fmt"
	"github.com/dropbox/changes-client/client"
	"github.com/dropbox/changes-client/client/adapter"
	"github.com/dropbox/changes-client/reporter"
	"log"
	"os"
	"os/signal"
	"sync"

	_ "github.com/dropbox/changes-client/adapter/basic"
	_ "github.com/dropbox/changes-client/adapter/lxc"
)

const (
	STATUS_QUEUED      = "queued"
	STATUS_IN_PROGRESS = "in_progress"
	STATUS_FINISHED    = "finished"

	RESULT_PASSED  = "passed"
	RESULT_FAILED  = "failed"
	RESULT_ABORTED = "aborted"

	SNAPSHOT_ACTIVE = "active"
	SNAPSHOT_FAILED = "failed'"
)

var (
	selectedAdapter string
	outputSnapshot  string
)

type Engine struct {
	config    *client.Config
	clientLog *client.Log
	adapter   adapter.Adapter
}

func RunBuildPlan(config *client.Config) error {
	var err error

	currentAdapter, err := adapter.Get(selectedAdapter)
	if err != nil {
		// TODO(dcramer): handle this error
		return err
	}

	engine := &Engine{
		config:    config,
		clientLog: client.NewLog(),
		adapter:   currentAdapter,
	}

	return engine.Run()
}

func (e *Engine) Run() error {
	var err error

	r := reporter.NewReporter(e.config.Server, e.config.JobstepID, e.config.Debug)
	defer r.Shutdown()

	wg := sync.WaitGroup{}

	wg.Add(1)
	go func() {
		reportLogChunks("console", e.clientLog, r)
		wg.Done()
	}()

	r.PushJobstepStatus(STATUS_IN_PROGRESS, "")

	err = e.adapter.Init(e.config)
	if err != nil {
		log.Print(fmt.Sprintf("[adapter] %s", err.Error()))
		r.PushJobstepStatus(STATUS_FINISHED, RESULT_FAILED)
		e.clientLog.Close()
		return err
	}

	err = e.adapter.Prepare(e.clientLog)
	// its important that shutdown happens in the same context as reportLogChunks
	// that is, we need all data from reporter to be sent before calling Shutdown
	defer e.adapter.Shutdown(e.clientLog)
	if err != nil {
		log.Print(fmt.Sprintf("[adapter] %s", err.Error()))
		r.PushJobstepStatus(STATUS_FINISHED, RESULT_FAILED)
		e.clientLog.Close()
		return err
	}

	result := e.runBuildPlan(r)

	if result == RESULT_PASSED && outputSnapshot != "" {
		err = e.captureSnapshot()
		if err != nil {
			r.PushSnapshotImageStatus(outputSnapshot, SNAPSHOT_FAILED)
		} else {
			r.PushSnapshotImageStatus(outputSnapshot, SNAPSHOT_ACTIVE)
		}
	}

	r.PushJobstepStatus(STATUS_FINISHED, result)

	e.clientLog.Close()

	wg.Wait()

	return err
}

func (e *Engine) executeCommands(r *reporter.Reporter) string {
	var result string = RESULT_PASSED

	wg := sync.WaitGroup{}

	for _, cmdConfig := range e.config.Cmds {
		cmd, err := client.NewCommand(cmdConfig.ID, cmdConfig.Script)
		if err != nil {
			r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 255)
			result = RESULT_FAILED
			break
		}
		r.PushCommandStatus(cmd.ID, STATUS_IN_PROGRESS, -1)

		cmd.CaptureOutput = cmdConfig.CaptureOutput

		env := os.Environ()
		for k, v := range cmdConfig.Env {
			env = append(env, k+"="+v)
		}
		cmd.Env = env

		if len(cmdConfig.Cwd) > 0 {
			cmd.Cwd = cmdConfig.Cwd
		}

		cmdResult, err := e.adapter.Run(cmd, e.clientLog)

		if err != nil {
			r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 255)
			result = RESULT_FAILED
		} else {
			if cmdResult.Success {
				if cmd.CaptureOutput {
					r.PushCommandOutput(cmd.ID, STATUS_FINISHED, 0, cmdResult.Output)
				} else {
					r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 0)
				}
			} else {
				r.PushCommandStatus(cmd.ID, STATUS_FINISHED, 1)
				result = RESULT_FAILED
			}
		}

		wg.Add(1)
		go func(artifacts []string) {
			e.publishArtifacts(r, artifacts)
			wg.Done()
		}(cmdConfig.Artifacts)

		if result == RESULT_FAILED {
			break
		}
	}

	wg.Wait()

	return result
}

func (e *Engine) captureSnapshot() error {
	log.Printf("[adapter] Capturing snapshot %s", outputSnapshot)
	err := e.adapter.CaptureSnapshot(outputSnapshot, e.clientLog)
	if err != nil {
		log.Printf("[adapter] Failed to capture snapshot: %s", err.Error())
		return err
	}
	return nil
}

func (e *Engine) runBuildPlan(r *reporter.Reporter) string {
	var result string

	// cancellation signal
	cancel := make(chan struct{})

	// capture ctrl+c and enforce a clean shutdown
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)
	go func() {
		shuttingDown := false
		for _ = range sigchan {
			if shuttingDown {
				log.Printf("Second interrupt received. Terminating!")
				os.Exit(1)
			}

			shuttingDown = true

			log.Printf("Interrupted! Cancelling execution and cleaning up..")
			cancel <- struct{}{}
		}
	}()

	// We need to ensure that we're able to abort the build if upstream suggests
	// that it's been cancelled.
	if !e.config.Debug {
		go func() {
			um := &UpstreamMonitor{
				Config: e.config,
			}
			err := um.WaitUntilAbort()
			if err != nil {
				cancel <- struct{}{}
			}
		}()
	}

	// actually begin executing the build plan
	finished := make(chan struct{})
	go func() {
		result = e.executeCommands(r)
		finished <- struct{}{}
	}()

	select {
	case <-finished:
	case <-cancel:
		e.clientLog.Writeln("==> ERROR: Build was aborted by upstream")
		result = RESULT_ABORTED
	}

	return result

}

func (e *Engine) publishArtifacts(r *reporter.Reporter, artifacts []string) {
	if len(artifacts) == 0 {
		e.clientLog.Writeln("==> Skipping artifact collection")
		return
	}

	e.clientLog.Writeln(fmt.Sprintf("==> Collecting artifacts matching %s", artifacts))

	matches, err := e.adapter.CollectArtifacts(artifacts, e.clientLog)
	if err != nil {
		e.clientLog.Writeln(fmt.Sprintf("==> ERROR: " + err.Error()))
		return
	}

	e.clientLog.Writeln(fmt.Sprintf("==> Found %d matching artifacts", len(matches)))

	r.PushArtifacts(matches)
}

func reportLogChunks(name string, clientLog *client.Log, r *reporter.Reporter) {
	for chunk := range clientLog.Chan {
		r.PushLogChunk(name, chunk)
	}
}

func init() {
	flag.StringVar(&selectedAdapter, "adapter", "basic", "Adapter to run build against")
	flag.StringVar(&outputSnapshot, "save-snapshot", "", "Save the resulting container snapshot")
}
<|endoftext|>


<commit_before>package main

import(
	"github.com/gophergala/go_ne/core"
	"log"
	"time"
	"fmt"
	"net/http"
	"io"
	"encoding/json"
	"crypto/sha1"
)

type EventsMonitor struct {
}

func NewEventsMonitor(config *core.Config, web *Web) (*EventsMonitor, error) {
	em := EventsMonitor{}

	for triggerName, event := range config.Triggers {
		log.Printf("Loading trigger [%s]\n", triggerName)

		switch event.Type {
			case `webhook`:
				err := em.createWebHook(config, triggerName, event, web); if err != nil {
					return nil, err
				}

			case `github-webhook`:
				err := em.createGitHubWebHook(config, triggerName, event, web); if err != nil {
					return nil, err
				}

			case `periodic`:
				err := em.createPeriodic(config, triggerName, event); if err != nil {
					return nil, err
				}
		}
	}

	return &em, nil
}

func (em *EventsMonitor) createWebHook(config *core.Config, triggerName string, event core.ConfigEvent, web *Web) (error) {
	web.mux.Get(web.webFolder + "/triggers/" + event.Endpoint, func(w http.ResponseWriter, r *http.Request) {
		err := em.runTask(config, event.Task); if err != nil {
			log.Printf("Webhook [start]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Persistent Logging
			log.Printf("%s", err)

			log.Printf("WebHook [failed]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Server error response?
			return
		}

		log.Printf("Webhook [complete]: %s for %s\n", triggerName, event.ServerGroup)
		io.WriteString(w, "OK")
	})

	return nil
}

type githubHookData struct {
	Ref string
}

//'push'
func (em *EventsMonitor) createGitHubWebHook(config *core.Config, triggerName string, event core.ConfigEvent, web *Web) (error) {
	web.mux.Post(web.webFolder + "/triggers/" + event.Endpoint, func(w http.ResponseWriter, r *http.Request) {
		fmt.Printf("%+v", r.Header)

		if(r.Header.Get("Content-Type") != `application/json` || r.Header.Get("X-Github-Event") != `push`) {
			// #TODO: Log
			return
		}

	// If we've specified a secret in the YAML then check it...
		if(event.Secret != "") {
			requestSecret := r.Header.Get("X-Hub-Signature")
			if(requestSecret == "") {
				// #TODO: Log
				return
			}

			secretSha1 := sha1.Sum([]byte(event.Secret))
			if(fmt.Sprintf("%s", secretSha1) != requestSecret) {
				// #TODO: Log
				return
			}
		}

		log.Printf("\n\n%+v\n", r.Body)

		decoder := json.NewDecoder(r.Body)

		var t githubHookData
		err := decoder.Decode(&t); if err != nil {
			fmt.Print(err)
		}

		log.Printf("%+v", t)

		err = em.runTask(config, event.Task); if err != nil {
			log.Printf("GitHub Webhook [start]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Persistent Logging
			log.Printf("%s", err)

			log.Printf("GitHub WebHook [failed]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Server error response?
			return
		}

		log.Printf("GitHub Webhook [complete]: %s for %s\n", triggerName, event.ServerGroup)
		io.WriteString(w, "OK")
	})

	return nil
}

func (em *EventsMonitor) createPeriodic(config *core.Config, triggerName string, event core.ConfigEvent) (error) {
	ticker := time.NewTicker(time.Duration(event.Period) * time.Second)

	go func() {
		for {
			select {
				case <- ticker.C:
					log.Printf("Periodic [start]: %s for %s\n", triggerName, event.ServerGroup)

					err := em.runTask(config, event.Task); if err != nil {
						// #TODO: Persistent Logging
						log.Printf("%s", err)

						log.Printf("Periodic [failed]: %s for %s\n", triggerName, event.ServerGroup)
						continue
					}

					log.Printf("Periodic [complete]: %s for %s\n", triggerName, event.ServerGroup)
			}
		}
	}()

	return nil
}

func (em *EventsMonitor) runTask(config *core.Config, task string) (error) {
	runner, err := NewLocalRunner(); if err != nil {
		return err
	}

	// #TODO: improve [/dev/null it for the moment!]
	go func() {
		for {
			select {
				case out := <-runner.chStdOut:
					log.Printf("%s", out)
				case out := <-runner.chStdErr:
					log.Printf("%s", out)
			}
		}
	}()

	err = core.RunTask(runner, config, task); if err != nil {
		return err
	}

	return nil
}
<commit_msg>Fixed SHA1 for github hooks<commit_after>package main

import(
	"github.com/gophergala/go_ne/core"
	"log"
	"time"
	"fmt"
	"net/http"
	"io"
	"io/ioutil"
	"encoding/json"
	"crypto/sha1"
	"crypto/hmac"
	"encoding/hex"
)

type EventsMonitor struct {
}

func NewEventsMonitor(config *core.Config, web *Web) (*EventsMonitor, error) {
	em := EventsMonitor{}

	for triggerName, event := range config.Triggers {
		log.Printf("Loading trigger [%s]\n", triggerName)

		switch event.Type {
			case `webhook`:
				err := em.createWebHook(config, triggerName, event, web); if err != nil {
					return nil, err
				}

			case `github-webhook`:
				err := em.createGitHubWebHook(config, triggerName, event, web); if err != nil {
					return nil, err
				}

			case `periodic`:
				err := em.createPeriodic(config, triggerName, event); if err != nil {
					return nil, err
				}
		}
	}

	return &em, nil
}

func (em *EventsMonitor) createWebHook(config *core.Config, triggerName string, event core.ConfigEvent, web *Web) (error) {
	web.mux.Get(web.webFolder + "/triggers/" + event.Endpoint, func(w http.ResponseWriter, r *http.Request) {
		err := em.runTask(config, event.Task); if err != nil {
			log.Printf("Webhook [start]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Persistent Logging
			log.Printf("%s", err)

			log.Printf("WebHook [failed]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Server error response?
			return
		}

		log.Printf("Webhook [complete]: %s for %s\n", triggerName, event.ServerGroup)
		io.WriteString(w, "OK")
	})

	return nil
}

type githubHookData struct {
	Ref string
}

//'push'
func (em *EventsMonitor) createGitHubWebHook(config *core.Config, triggerName string, event core.ConfigEvent, web *Web) (error) {
	web.mux.Post(web.webFolder + "/triggers/" + event.Endpoint, func(w http.ResponseWriter, r *http.Request) {
		if(r.Header.Get("Content-Type") != `application/json` || r.Header.Get("X-Github-Event") != `push`) {
			// #TODO: Log
			return
		}

	// If we've specified a secret in the YAML then check it...
		if(event.Secret != "") {
			requestSecret := r.Header.Get("X-Hub-Signature")
			if(requestSecret == "") {
				// #TODO: Log
				return
			}

			body, err := ioutil.ReadAll(r.Body); if err != nil {
				// #TODO: Log
				return
			}

			key := []byte(event.Secret)
			h := hmac.New(sha1.New, key)
			h.Write(body)
			secretSha1 := fmt.Sprintf("sha1=%s", hex.EncodeToString(h.Sum(nil)))

			fmt.Printf("\n%s\n%s\n", secretSha1, requestSecret)
			if(secretSha1 != requestSecret) {
				// #TODO: Log
				return
			}
		}

		log.Printf("\n\n%+v\n", r.Body)

		decoder := json.NewDecoder(r.Body)

		var t githubHookData
		err := decoder.Decode(&t); if err != nil {
			fmt.Print(err)
		}

		log.Printf("%+v", t)

		err = em.runTask(config, event.Task); if err != nil {
			log.Printf("GitHub Webhook [start]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Persistent Logging
			log.Printf("%s", err)

			log.Printf("GitHub WebHook [failed]: %s for %s\n", triggerName, event.ServerGroup)

			// #TODO: Server error response?
			return
		}

		log.Printf("GitHub Webhook [complete]: %s for %s\n", triggerName, event.ServerGroup)
		io.WriteString(w, "OK")
	})

	return nil
}

func (em *EventsMonitor) createPeriodic(config *core.Config, triggerName string, event core.ConfigEvent) (error) {
	ticker := time.NewTicker(time.Duration(event.Period) * time.Second)

	go func() {
		for {
			select {
				case <- ticker.C:
					log.Printf("Periodic [start]: %s for %s\n", triggerName, event.ServerGroup)

					err := em.runTask(config, event.Task); if err != nil {
						// #TODO: Persistent Logging
						log.Printf("%s", err)

						log.Printf("Periodic [failed]: %s for %s\n", triggerName, event.ServerGroup)
						continue
					}

					log.Printf("Periodic [complete]: %s for %s\n", triggerName, event.ServerGroup)
			}
		}
	}()

	return nil
}

func (em *EventsMonitor) runTask(config *core.Config, task string) (error) {
	runner, err := NewLocalRunner(); if err != nil {
		return err
	}

	// #TODO: improve [/dev/null it for the moment!]
	go func() {
		for {
			select {
				case out := <-runner.chStdOut:
					log.Printf("%s", out)
				case out := <-runner.chStdErr:
					log.Printf("%s", out)
			}
		}
	}()

	err = core.RunTask(runner, config, task); if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package parser

import (
	"github.com/alecthomas/participle/lexer"

	"github.com/sleepinggenius2/gosmi/types"
)

type AgentCapabilityVariation struct {
	Pos lexer.Position

	Name        types.SmiIdentifier   `parser:"\"VARIATION\" @Ident"` // Required
	Syntax      *Syntax               `parser:"( \"SYNTAX\" @@ )?"`
	WriteSyntax *Syntax               `parser:"( \"WRITE-SYNTAX\" @@ )?"`
	Access      *Access               `parser:"( \"ACCESS\" @( \"write-only\" | \"not-implemented\" | \"accessible-for-notify\" | \"read-only\" | \"read-write\" | \"read-create\" ) )?"`
	Creation    []types.SmiIdentifier `parser:"( \"CREATION-REQUIRES\" \"{\" @Ident ( \",\" @Ident )* \",\"? \"}\" )?"`
	Defval      *string               `parser:"( \"DEFVAL\" \"{\" @( \"-\"? Int | BinString | HexString | Text | Ident | ( \"{\" ( Int+ | ( Ident ( \",\" Ident )* \",\"? )? ) \"}\" ) ) \"}\" )?"`
	Description string                `parser:"\"DESCRIPTION\" @Text"` // Required
}

type AgentCapabilityModule struct {
	Pos lexer.Position

	Module     types.SmiIdentifier        `parser:"\"SUPPORTS\" @Ident"`                                      // Required
	Includes   []types.SmiIdentifier      `parser:"\"INCLUDES\" \"{\" @Ident ( \",\" @Ident )* \",\"? \"}\""` // Required
	Variations []AgentCapabilityVariation `parser:"@@*"`
}

type AgentCapabilities struct {
	Pos lexer.Position

	ProductRelease string                  `parser:"\"PRODUCT-RELEASE\" @Text"`                  // Required
	Status         Status                  `parser:"\"STATUS\" @( \"current\" | \"obsolete\" )"` // Required
	Description    string                  `parser:"\"DESCRIPTION\" @Text"`                      // Required
	Reference      string                  `parser:"( \"REFERENCE\" @Text )?"`
	Modules        []AgentCapabilityModule `parser:"@@*"`
}

type ComplianceGroup struct {
	Pos lexer.Position

	Name        types.SmiIdentifier `parser:"\"GROUP\" @Ident"`
	Description string              `parser:"\"DESCRIPTION\" @Text"`
}

type ComplianceObject struct {
	Pos lexer.Position

	Name        types.SmiIdentifier `parser:"\"OBJECT\" @Ident"`
	Syntax      *Syntax             `parser:"( \"SYNTAX\" @@ )?"`
	WriteSyntax *Syntax             `parser:"( \"WRITE-SYNTAX\" @@ )?"`
	MinAccess   *Access             `parser:"( \"MIN-ACCESS\" @( \"not-accessible\" | \"accessible-for-notify\" | \"read-only\" | \"read-write\" | \"read-create\" ) )?"`
	Description string              `parser:"\"DESCRIPTION\" @Text"`
}

type Compliance struct {
	Pos lexer.Position

	Group  *ComplianceGroup  `parser:"@@"`
	Object *ComplianceObject `parser:"| @@"`
}

type ComplianceModuleName string

func (n *ComplianceModuleName) Parse(lex *lexer.PeekingLexer) error {
	token, err := lex.Peek(0)
	if err != nil {
		return err
	}
	if token.Type == smiLexer.Symbols()["Assign"] || token.Value == "MANDATORY-GROUPS" || token.Value == "GROUP" || token.Value == "OBJECT" {
		*n = ""
		return nil
	}
	token, err = lex.Next()
	if err != nil {
		return err
	}
	*n = ComplianceModuleName(token.Value)
	return nil
}

type ModuleComplianceModule struct {
	Pos lexer.Position

	Name            ComplianceModuleName  `parser:"@@"`
	MandatoryGroups []types.SmiIdentifier `parser:"( \"MANDATORY-GROUPS\" \"{\" @Ident ( \",\" @Ident )* \",\"? \"}\" )?"`
	Compliances     []Compliance          `parser:"@@*"`
}

type ModuleCompliance struct {
	Pos lexer.Position

	Status      Status                   `parser:"\"STATUS\" @( \"current\" | \"deprecated\" | \"obsolete\" )"` // Required
	Description string                   `parser:"\"DESCRIPTION\" @Text"`                                       // Required
	Reference   string                   `parser:"( \"REFERENCE\" @Text )?"`
	Modules     []ModuleComplianceModule `parser:"( \"MODULE\" @@ )+"`
}
<commit_msg>Adds "deprecated" STATUS value for AGENT-CAPABILITIES<commit_after>package parser

import (
	"github.com/alecthomas/participle/lexer"

	"github.com/sleepinggenius2/gosmi/types"
)

type AgentCapabilityVariation struct {
	Pos lexer.Position

	Name        types.SmiIdentifier   `parser:"\"VARIATION\" @Ident"` // Required
	Syntax      *Syntax               `parser:"( \"SYNTAX\" @@ )?"`
	WriteSyntax *Syntax               `parser:"( \"WRITE-SYNTAX\" @@ )?"`
	Access      *Access               `parser:"( \"ACCESS\" @( \"write-only\" | \"not-implemented\" | \"accessible-for-notify\" | \"read-only\" | \"read-write\" | \"read-create\" ) )?"`
	Creation    []types.SmiIdentifier `parser:"( \"CREATION-REQUIRES\" \"{\" @Ident ( \",\" @Ident )* \",\"? \"}\" )?"`
	Defval      *string               `parser:"( \"DEFVAL\" \"{\" @( \"-\"? Int | BinString | HexString | Text | Ident | ( \"{\" ( Int+ | ( Ident ( \",\" Ident )* \",\"? )? ) \"}\" ) ) \"}\" )?"`
	Description string                `parser:"\"DESCRIPTION\" @Text"` // Required
}

type AgentCapabilityModule struct {
	Pos lexer.Position

	Module     types.SmiIdentifier        `parser:"\"SUPPORTS\" @Ident"`                                      // Required
	Includes   []types.SmiIdentifier      `parser:"\"INCLUDES\" \"{\" @Ident ( \",\" @Ident )* \",\"? \"}\""` // Required
	Variations []AgentCapabilityVariation `parser:"@@*"`
}

type AgentCapabilities struct {
	Pos lexer.Position

	ProductRelease string                  `parser:"\"PRODUCT-RELEASE\" @Text"`                                   // Required
	Status         Status                  `parser:"\"STATUS\" @( \"current\" | \"deprecated\" | \"obsolete\" )"` // Required - RFC1444 Section 5.2 defines "deprecated" value
	Description    string                  `parser:"\"DESCRIPTION\" @Text"`                                       // Required
	Reference      string                  `parser:"( \"REFERENCE\" @Text )?"`
	Modules        []AgentCapabilityModule `parser:"@@*"`
}

type ComplianceGroup struct {
	Pos lexer.Position

	Name        types.SmiIdentifier `parser:"\"GROUP\" @Ident"`
	Description string              `parser:"\"DESCRIPTION\" @Text"`
}

type ComplianceObject struct {
	Pos lexer.Position

	Name        types.SmiIdentifier `parser:"\"OBJECT\" @Ident"`
	Syntax      *Syntax             `parser:"( \"SYNTAX\" @@ )?"`
	WriteSyntax *Syntax             `parser:"( \"WRITE-SYNTAX\" @@ )?"`
	MinAccess   *Access             `parser:"( \"MIN-ACCESS\" @( \"not-accessible\" | \"accessible-for-notify\" | \"read-only\" | \"read-write\" | \"read-create\" ) )?"`
	Description string              `parser:"\"DESCRIPTION\" @Text"`
}

type Compliance struct {
	Pos lexer.Position

	Group  *ComplianceGroup  `parser:"@@"`
	Object *ComplianceObject `parser:"| @@"`
}

type ComplianceModuleName string

func (n *ComplianceModuleName) Parse(lex *lexer.PeekingLexer) error {
	token, err := lex.Peek(0)
	if err != nil {
		return err
	}
	if token.Type == smiLexer.Symbols()["Assign"] || token.Value == "MANDATORY-GROUPS" || token.Value == "GROUP" || token.Value == "OBJECT" {
		*n = ""
		return nil
	}
	token, err = lex.Next()
	if err != nil {
		return err
	}
	*n = ComplianceModuleName(token.Value)
	return nil
}

type ModuleComplianceModule struct {
	Pos lexer.Position

	Name            ComplianceModuleName  `parser:"@@"`
	MandatoryGroups []types.SmiIdentifier `parser:"( \"MANDATORY-GROUPS\" \"{\" @Ident ( \",\" @Ident )* \",\"? \"}\" )?"`
	Compliances     []Compliance          `parser:"@@*"`
}

type ModuleCompliance struct {
	Pos lexer.Position

	Status      Status                   `parser:"\"STATUS\" @( \"current\" | \"deprecated\" | \"obsolete\" )"` // Required - RFC1444 Section 4.1 defines "deprecated" value
	Description string                   `parser:"\"DESCRIPTION\" @Text"`                                       // Required
	Reference   string                   `parser:"( \"REFERENCE\" @Text )?"`
	Modules     []ModuleComplianceModule `parser:"( \"MODULE\" @@ )+"`
}
<|endoftext|>