<commit_before>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	stdlog "log"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/alecthomas/kingpin"
	contracts "github.com/estafette/estafette-ci-contracts"
	crypt "github.com/estafette/estafette-ci-crypt"
	manifest "github.com/estafette/estafette-ci-manifest"
	"github.com/opentracing/opentracing-go"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
)

var (
	app       string
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()

	builderConfigFlag         = kingpin.Flag("builder-config", "The Estafette server passes in this json structure to parameterize the build, set trusted images and inject credentials.").Envar("BUILDER_CONFIG").String()
	secretDecryptionKey       = kingpin.Flag("secret-decryption-key", "The AES-256 key used to decrypt secrets that have been encrypted with it.").Envar("SECRET_DECRYPTION_KEY").String()
	secretDecryptionKeyBase64 = kingpin.Flag("secret-decryption-key-base64", "The base64 encoded AES-256 key used to decrypt secrets that have been encrypted with it.").Envar("SECRET_DECRYPTION_KEY_BASE64").String()
	runAsJob                  = kingpin.Flag("run-as-job", "To run the builder as a job and prevent build failures to fail the job.").Default("false").OverrideDefaultFromEnvar("RUN_AS_JOB").Bool()
)

func main() {

	// parse command line parameters
	kingpin.Parse()

	// define channel to catch SIGTERM and send out cancellation to stop further execution of stages and send the final state and logs to the ci server
	osSignals := make(chan os.Signal, 1)
	signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM)
	cancellationChannel := make(chan struct{})
	go func(osSignals chan os.Signal, cancellationChannel chan struct{}) {
		// wait for sigterm
		<-osSignals
		// broadcast a cancellation
		close(cancellationChannel)
	}(osSignals, cancellationChannel)

	// support both base64 encoded decryption key and non-encoded
	secretDecryptionKeyBase64Encoded := *secretDecryptionKeyBase64 != ""
	decryptionKey := *secretDecryptionKey
	if secretDecryptionKeyBase64Encoded {
		decryptionKey = *secretDecryptionKeyBase64
	}

	secretHelper := crypt.NewSecretHelper(decryptionKey, secretDecryptionKeyBase64Encoded)

	// read builder config from envvar and unset envar; will replace parameterizing the job via separate envvars
	var builderConfig contracts.BuilderConfig
	builderConfigJSON := *builderConfigFlag
	if builderConfigJSON == "" {
		log.Fatal().Msg("BUILDER_CONFIG envvar is not set")
	}
	os.Unsetenv("BUILDER_CONFIG")

	// unmarshal builder config
	err := json.Unmarshal([]byte(builderConfigJSON), &builderConfig)
	if err != nil {
		log.Fatal().Err(err).Interface("builderConfigJSON", builderConfigJSON).Msg("Failed to unmarshal BUILDER_CONFIG")
	}

	// decrypt all credentials
	decryptedCredentials := []*contracts.CredentialConfig{}
	for _, c := range builderConfig.Credentials {

		// loop all additional properties and decrypt
		decryptedAdditionalProperties := map[string]interface{}{}
		for key, value := range c.AdditionalProperties {
			if s, isString := value.(string); isString {
				decryptedAdditionalProperties[key], err = secretHelper.DecryptAllEnvelopes(s)
				if err != nil {
					log.Fatal().Err(err).Msgf("Failed decrypting credential %v property %v", c.Name, key)
				}
			} else {
				decryptedAdditionalProperties[key] = value
			}
		}
		c.AdditionalProperties = decryptedAdditionalProperties

		decryptedCredentials = append(decryptedCredentials, c)
	}
	builderConfig.Credentials = decryptedCredentials

	// bootstrap
	obfuscator := NewObfuscator(secretHelper)
	envvarHelper := NewEnvvarHelper("ESTAFETTE_", secretHelper, obfuscator)
	whenEvaluator := NewWhenEvaluator(envvarHelper)
	dockerRunner := NewDockerRunner(envvarHelper, obfuscator, *runAsJob, builderConfig, cancellationChannel)
	pipelineRunner := NewPipelineRunner(envvarHelper, whenEvaluator, dockerRunner, *runAsJob, cancellationChannel)
	endOfLifeHelper := NewEndOfLifeHelper(*runAsJob, builderConfig)

	// detect controlling server
	ciServer := envvarHelper.getCiServer()

	if ciServer == "estafette" {
		// unset all ESTAFETTE_ envvars so they don't get abused by non-estafette components
		envvarHelper.unsetEstafetteEnvvars()
	}

	if ciServer == "gocd" {

		fatalHandler := NewGocdFatalHandler()

		// pretty print for go.cd integration
		log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().
			Timestamp().
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msgf("Starting %v version %v...", app, version)

		// create docker client
		_, err := dockerRunner.createDockerClient()
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Failed creating a docker client")
		}

		// read yaml
		manifest, err := manifest.ReadManifestFromFile(".estafette.yaml")
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Reading .estafette.yaml manifest failed")
		}

		// initialize obfuscator
		err = obfuscator.CollectSecrets(manifest)
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Collecting secrets to obfuscate failed")
		}

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Getting current working directory failed")
		}

		log.Info().Msgf("Running %v stages", len(manifest.Stages))

		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Setting global environment variables failed")
		}
		err = envvarHelper.setEstafetteStagesEnvvar(manifest.Stages)
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Setting ESTAFETTE_STAGES environment variable failed")
		}

		// collect estafette and 'global' envvars from manifest
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvarsAndLabels(manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(manifest)

		// merge estafette and global envvars
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)

		// run stages
		result, err := pipelineRunner.runStages(context.Background(), manifest.Stages, dir, envvars)
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Executing stages from manifest failed")
		}

		renderStats(result)

		handleExit(result)

	} else if ciServer == "estafette" {

		// log as severity for stackdriver logging to recognize the level
		zerolog.LevelFieldName = "severity"

		closer := initJaeger(app)
		defer closer.Close()

		envvarHelper.setEstafetteBuilderConfigEnvvars(builderConfig)

		buildLog := contracts.BuildLog{
			RepoSource:   builderConfig.Git.RepoSource,
			RepoOwner:    builderConfig.Git.RepoOwner,
			RepoName:     builderConfig.Git.RepoName,
			RepoBranch:   builderConfig.Git.RepoBranch,
			RepoRevision: builderConfig.Git.RepoRevision,
			Steps:        make([]contracts.BuildLogStep, 0),
		}

		// set some default fields added to all logs
		log.Logger = zerolog.New(os.Stdout).With().
			Timestamp().
			Str("app", app).
			Str("version", version).
			Str("jobName", *builderConfig.JobName).
			Interface("git", builderConfig.Git).
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msgf("Starting %v version %v...", app, version)

		rootSpanName := "RunBuildJob"
		if *builderConfig.Action == "release" {
			rootSpanName = "RunReleaseJob"
		}

		rootSpan := opentracing.StartSpan(rootSpanName)
		defer rootSpan.Finish()

		ctx := context.Background()
		ctx = opentracing.ContextWithSpan(ctx, rootSpan)

		// start docker daemon
		dockerDaemonStartSpan, _ := opentracing.StartSpanFromContext(ctx, "StartDockerDaemon")
		err = dockerRunner.startDockerDaemon()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Error starting docker daemon")
		}

		// wait for docker daemon to be ready for usage
		dockerRunner.waitForDockerDaemon()
		dockerDaemonStartSpan.Finish()

		// listen to cancellation in order to stop any running pipeline or container
		go pipelineRunner.stopPipelineOnCancellation()
		go dockerRunner.stopContainerOnCancellation()

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Getting current working directory failed")
		}

		// set some envvars
		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Setting global environment variables failed")
		}

		// initialize obfuscator
		err = obfuscator.CollectSecrets(*builderConfig.Manifest)
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Collecting secrets to obfuscate failed")
		}

		// check whether this is a regular build or a release
		stages := builderConfig.Manifest.Stages
		if *builderConfig.Action == "release" {
			// check if the release is defined
			releaseExists := false
			for _, r := range builderConfig.Manifest.Releases {
				if r.Name == builderConfig.ReleaseParams.ReleaseName {
					releaseExists = true
					stages = r.Stages
				}
			}
			if !releaseExists {
				endOfLifeHelper.handleFatal(ctx, buildLog, nil, fmt.Sprintf("Release %v does not exist", builderConfig.ReleaseParams.ReleaseName))
			}
			log.Info().Msgf("Starting release %v at version %v...", builderConfig.ReleaseParams.ReleaseName, builderConfig.BuildVersion.Version)
		} else {
			log.Info().Msgf("Starting build version %v...", builderConfig.BuildVersion.Version)
		}

		err = envvarHelper.setEstafetteStagesEnvvar(stages)
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Setting ESTAFETTE_STAGES environment variable failed")
		}

		// create docker client
		_, err = dockerRunner.createDockerClient()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Failed creating a docker client")
		}

		// collect estafette envvars and run stages from manifest
		log.Info().Msgf("Running %v stages", len(stages))
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvarsAndLabels(*builderConfig.Manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(*builderConfig.Manifest)
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)

		// run stages
		result, err := pipelineRunner.runStages(ctx, stages, dir, envvars)
		if err != nil && !result.canceled {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Executing stages from manifest failed")
		}

		// send result to ci-api
		log.Info().Interface("result", result).Msg("Finished running stages")
		buildLog.Steps = transformPipelineRunResultToBuildLogSteps(estafetteEnvvars, result)
		buildStatus := "succeeded"
		if result.HasAggregatedErrors() {
			buildStatus = "failed"
		}
		if result.canceled {
			buildStatus = "canceled"
		}

		_ = endOfLifeHelper.sendBuildFinishedEvent(ctx, buildStatus)
		_ = endOfLifeHelper.sendBuildJobLogEvent(ctx, buildLog)
		_ = endOfLifeHelper.sendBuildCleanEvent(ctx, buildStatus)

		// finish and flush so it gets sent to the tracing backend
		rootSpan.Finish()
		closer.Close()

		if *runAsJob {
			os.Exit(0)
		} else {
			handleExit(result)
		}

	} else {
		// Set up a simple console logger
		log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().
			Timestamp().
			Logger()

		log.Warn().Msgf("The CI Server (\"%s\") is not recognized, exiting.", ciServer)
	}
}

// initJaeger returns an instance of Jaeger Tracer that can be configured with environment variables
// https://github.com/jaegertracing/jaeger-client-go#environment-variables
func initJaeger(service string) io.Closer {

	cfg, err := jaegercfg.FromEnv()
	if err != nil {
		log.Fatal().Err(err).Msg("Generating Jaeger config from environment variables failed")
	}

	if os.Getenv("JAEGER_AGENT_HOST") != "" {
		// get remote config from jaeger-agent running as daemonset
		if cfg != nil && cfg.Sampler != nil && cfg.Sampler.SamplingServerURL == "" {
			cfg.Sampler.SamplingServerURL = fmt.Sprintf("http://%v:5778/sampling", os.Getenv("JAEGER_AGENT_HOST"))
		}

		// get remote config for baggage restrictions from jaeger-agent running as deamonset
		if cfg != nil && cfg.BaggageRestrictions != nil && cfg.BaggageRestrictions.HostPort == "" {
			cfg.BaggageRestrictions.HostPort = fmt.Sprintf("%v:5778", os.Getenv("JAEGER_AGENT_HOST"))
		}
	}

	closer, err := cfg.InitGlobalTracer(service, jaegercfg.Logger(jaeger.StdLogger))

	if err != nil {
		log.Fatal().Err(err).Msg("Generating Jaeger tracer failed")
	}

	return closer
}
<commit_msg>no remote config<commit_after>package main

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	stdlog "log"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/alecthomas/kingpin"
	contracts "github.com/estafette/estafette-ci-contracts"
	crypt "github.com/estafette/estafette-ci-crypt"
	manifest "github.com/estafette/estafette-ci-manifest"
	"github.com/opentracing/opentracing-go"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
	"github.com/uber/jaeger-client-go"
	jaegercfg "github.com/uber/jaeger-client-go/config"
)

var (
	app       string
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()

	builderConfigFlag         = kingpin.Flag("builder-config", "The Estafette server passes in this json structure to parameterize the build, set trusted images and inject credentials.").Envar("BUILDER_CONFIG").String()
	secretDecryptionKey       = kingpin.Flag("secret-decryption-key", "The AES-256 key used to decrypt secrets that have been encrypted with it.").Envar("SECRET_DECRYPTION_KEY").String()
	secretDecryptionKeyBase64 = kingpin.Flag("secret-decryption-key-base64", "The base64 encoded AES-256 key used to decrypt secrets that have been encrypted with it.").Envar("SECRET_DECRYPTION_KEY_BASE64").String()
	runAsJob                  = kingpin.Flag("run-as-job", "To run the builder as a job and prevent build failures to fail the job.").Default("false").OverrideDefaultFromEnvar("RUN_AS_JOB").Bool()
)

func main() {

	// parse command line parameters
	kingpin.Parse()

	// define channel to catch SIGTERM and send out cancellation to stop further execution of stages and send the final state and logs to the ci server
	osSignals := make(chan os.Signal, 1)
	signal.Notify(osSignals, os.Interrupt, syscall.SIGTERM)
	cancellationChannel := make(chan struct{})
	go func(osSignals chan os.Signal, cancellationChannel chan struct{}) {
		// wait for sigterm
		<-osSignals
		// broadcast a cancellation
		close(cancellationChannel)
	}(osSignals, cancellationChannel)

	// support both base64 encoded decryption key and non-encoded
	secretDecryptionKeyBase64Encoded := *secretDecryptionKeyBase64 != ""
	decryptionKey := *secretDecryptionKey
	if secretDecryptionKeyBase64Encoded {
		decryptionKey = *secretDecryptionKeyBase64
	}

	secretHelper := crypt.NewSecretHelper(decryptionKey, secretDecryptionKeyBase64Encoded)

	// read builder config from envvar and unset envar; will replace parameterizing the job via separate envvars
	var builderConfig contracts.BuilderConfig
	builderConfigJSON := *builderConfigFlag
	if builderConfigJSON == "" {
		log.Fatal().Msg("BUILDER_CONFIG envvar is not set")
	}
	os.Unsetenv("BUILDER_CONFIG")

	// unmarshal builder config
	err := json.Unmarshal([]byte(builderConfigJSON), &builderConfig)
	if err != nil {
		log.Fatal().Err(err).Interface("builderConfigJSON", builderConfigJSON).Msg("Failed to unmarshal BUILDER_CONFIG")
	}

	// decrypt all credentials
	decryptedCredentials := []*contracts.CredentialConfig{}
	for _, c := range builderConfig.Credentials {

		// loop all additional properties and decrypt
		decryptedAdditionalProperties := map[string]interface{}{}
		for key, value := range c.AdditionalProperties {
			if s, isString := value.(string); isString {
				decryptedAdditionalProperties[key], err = secretHelper.DecryptAllEnvelopes(s)
				if err != nil {
					log.Fatal().Err(err).Msgf("Failed decrypting credential %v property %v", c.Name, key)
				}
			} else {
				decryptedAdditionalProperties[key] = value
			}
		}
		c.AdditionalProperties = decryptedAdditionalProperties

		decryptedCredentials = append(decryptedCredentials, c)
	}
	builderConfig.Credentials = decryptedCredentials

	// bootstrap
	obfuscator := NewObfuscator(secretHelper)
	envvarHelper := NewEnvvarHelper("ESTAFETTE_", secretHelper, obfuscator)
	whenEvaluator := NewWhenEvaluator(envvarHelper)
	dockerRunner := NewDockerRunner(envvarHelper, obfuscator, *runAsJob, builderConfig, cancellationChannel)
	pipelineRunner := NewPipelineRunner(envvarHelper, whenEvaluator, dockerRunner, *runAsJob, cancellationChannel)
	endOfLifeHelper := NewEndOfLifeHelper(*runAsJob, builderConfig)

	// detect controlling server
	ciServer := envvarHelper.getCiServer()

	if ciServer == "estafette" {
		// unset all ESTAFETTE_ envvars so they don't get abused by non-estafette components
		envvarHelper.unsetEstafetteEnvvars()
	}

	if ciServer == "gocd" {

		fatalHandler := NewGocdFatalHandler()

		// pretty print for go.cd integration
		log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().
			Timestamp().
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msgf("Starting %v version %v...", app, version)

		// create docker client
		_, err := dockerRunner.createDockerClient()
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Failed creating a docker client")
		}

		// read yaml
		manifest, err := manifest.ReadManifestFromFile(".estafette.yaml")
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Reading .estafette.yaml manifest failed")
		}

		// initialize obfuscator
		err = obfuscator.CollectSecrets(manifest)
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Collecting secrets to obfuscate failed")
		}

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Getting current working directory failed")
		}

		log.Info().Msgf("Running %v stages", len(manifest.Stages))

		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Setting global environment variables failed")
		}
		err = envvarHelper.setEstafetteStagesEnvvar(manifest.Stages)
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Setting ESTAFETTE_STAGES environment variable failed")
		}

		// collect estafette and 'global' envvars from manifest
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvarsAndLabels(manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(manifest)

		// merge estafette and global envvars
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)

		// run stages
		result, err := pipelineRunner.runStages(context.Background(), manifest.Stages, dir, envvars)
		if err != nil {
			fatalHandler.handleGocdFatal(err, "Executing stages from manifest failed")
		}

		renderStats(result)

		handleExit(result)

	} else if ciServer == "estafette" {

		// log as severity for stackdriver logging to recognize the level
		zerolog.LevelFieldName = "severity"

		closer := initJaeger(app)
		defer closer.Close()

		envvarHelper.setEstafetteBuilderConfigEnvvars(builderConfig)

		buildLog := contracts.BuildLog{
			RepoSource:   builderConfig.Git.RepoSource,
			RepoOwner:    builderConfig.Git.RepoOwner,
			RepoName:     builderConfig.Git.RepoName,
			RepoBranch:   builderConfig.Git.RepoBranch,
			RepoRevision: builderConfig.Git.RepoRevision,
			Steps:        make([]contracts.BuildLogStep, 0),
		}

		// set some default fields added to all logs
		log.Logger = zerolog.New(os.Stdout).With().
			Timestamp().
			Str("app", app).
			Str("version", version).
			Str("jobName", *builderConfig.JobName).
			Interface("git", builderConfig.Git).
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msgf("Starting %v version %v...", app, version)

		rootSpanName := "RunBuildJob"
		if *builderConfig.Action == "release" {
			rootSpanName = "RunReleaseJob"
		}

		rootSpan := opentracing.StartSpan(rootSpanName)
		defer rootSpan.Finish()

		ctx := context.Background()
		ctx = opentracing.ContextWithSpan(ctx, rootSpan)

		// start docker daemon
		dockerDaemonStartSpan, _ := opentracing.StartSpanFromContext(ctx, "StartDockerDaemon")
		err = dockerRunner.startDockerDaemon()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Error starting docker daemon")
		}

		// wait for docker daemon to be ready for usage
		dockerRunner.waitForDockerDaemon()
		dockerDaemonStartSpan.Finish()

		// listen to cancellation in order to stop any running pipeline or container
		go pipelineRunner.stopPipelineOnCancellation()
		go dockerRunner.stopContainerOnCancellation()

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Getting current working directory failed")
		}

		// set some envvars
		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Setting global environment variables failed")
		}

		// initialize obfuscator
		err = obfuscator.CollectSecrets(*builderConfig.Manifest)
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Collecting secrets to obfuscate failed")
		}

		// check whether this is a regular build or a release
		stages := builderConfig.Manifest.Stages
		if *builderConfig.Action == "release" {
			// check if the release is defined
			releaseExists := false
			for _, r := range builderConfig.Manifest.Releases {
				if r.Name == builderConfig.ReleaseParams.ReleaseName {
					releaseExists = true
					stages = r.Stages
				}
			}
			if !releaseExists {
				endOfLifeHelper.handleFatal(ctx, buildLog, nil, fmt.Sprintf("Release %v does not exist", builderConfig.ReleaseParams.ReleaseName))
			}
			log.Info().Msgf("Starting release %v at version %v...", builderConfig.ReleaseParams.ReleaseName, builderConfig.BuildVersion.Version)
		} else {
			log.Info().Msgf("Starting build version %v...", builderConfig.BuildVersion.Version)
		}

		err = envvarHelper.setEstafetteStagesEnvvar(stages)
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Setting ESTAFETTE_STAGES environment variable failed")
		}

		// create docker client
		_, err = dockerRunner.createDockerClient()
		if err != nil {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Failed creating a docker client")
		}

		// collect estafette envvars and run stages from manifest
		log.Info().Msgf("Running %v stages", len(stages))
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvarsAndLabels(*builderConfig.Manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(*builderConfig.Manifest)
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)

		// run stages
		result, err := pipelineRunner.runStages(ctx, stages, dir, envvars)
		if err != nil && !result.canceled {
			endOfLifeHelper.handleFatal(ctx, buildLog, err, "Executing stages from manifest failed")
		}

		// send result to ci-api
		log.Info().Interface("result", result).Msg("Finished running stages")
		buildLog.Steps = transformPipelineRunResultToBuildLogSteps(estafetteEnvvars, result)
		buildStatus := "succeeded"
		if result.HasAggregatedErrors() {
			buildStatus = "failed"
		}
		if result.canceled {
			buildStatus = "canceled"
		}

		_ = endOfLifeHelper.sendBuildFinishedEvent(ctx, buildStatus)
		_ = endOfLifeHelper.sendBuildJobLogEvent(ctx, buildLog)
		_ = endOfLifeHelper.sendBuildCleanEvent(ctx, buildStatus)

		// finish and flush so it gets sent to the tracing backend
		rootSpan.Finish()
		closer.Close()

		if *runAsJob {
			os.Exit(0)
		} else {
			handleExit(result)
		}

	} else {
		// Set up a simple console logger
		log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().
			Timestamp().
			Logger()

		log.Warn().Msgf("The CI Server (\"%s\") is not recognized, exiting.", ciServer)
	}
}

// initJaeger returns an instance of Jaeger Tracer that can be configured with environment variables
// https://github.com/jaegertracing/jaeger-client-go#environment-variables
func initJaeger(service string) io.Closer {

	cfg, err := jaegercfg.FromEnv()
	if err != nil {
		log.Fatal().Err(err).Msg("Generating Jaeger config from environment variables failed")
	}

	// if os.Getenv("JAEGER_AGENT_HOST") != "" {
	// 	// get remote config from jaeger-agent running as daemonset
	// 	if cfg != nil && cfg.Sampler != nil && cfg.Sampler.SamplingServerURL == "" {
	// 		cfg.Sampler.SamplingServerURL = fmt.Sprintf("http://%v:5778/sampling", os.Getenv("JAEGER_AGENT_HOST"))
	// 	}

	// 	// get remote config for baggage restrictions from jaeger-agent running as deamonset
	// 	if cfg != nil && cfg.BaggageRestrictions != nil && cfg.BaggageRestrictions.HostPort == "" {
	// 		cfg.BaggageRestrictions.HostPort = fmt.Sprintf("%v:5778", os.Getenv("JAEGER_AGENT_HOST"))
	// 	}
	// }

	closer, err := cfg.InitGlobalTracer(service, jaegercfg.Logger(jaeger.StdLogger))

	if err != nil {
		log.Fatal().Err(err).Msg("Generating Jaeger tracer failed")
	}

	return closer
}
<|endoftext|>


<commit_before>package mock

import (
	"github.com/kvstore"
	"github.com/stretchr/testify/mock"
)

// Mock store. Mocks all Store functions using testify.Mock
type Mock struct {
	mock.Mock

	// Endpoints passed to InitializeMock
	Endpoints []string

	// Options passed to InitializeMock
	Options *store.Config
}

// New creates a Mock store
func New(endpoints []string, options *store.Config) (store.Store, error) {
	s := &Mock{}
	s.Endpoints = endpoints
	s.Options = options
	return s, nil
}

// Put mock
func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error {
	args := s.Mock.Called(key, value, opts)
	return args.Error(0)
}

// Get mock
func (s *Mock) Get(key string) (*store.KVPair, error) {
	args := s.Mock.Called(key)
	return args.Get(0).(*store.KVPair), args.Error(1)
}

// Delete mock
func (s *Mock) Delete(key string) error {
	args := s.Mock.Called(key)
	return args.Error(0)
}

// Exists mock
func (s *Mock) Exists(key string) (bool, error) {
	args := s.Mock.Called(key)
	return args.Bool(0), args.Error(1)
}

// Watch mock
func (s *Mock) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
	args := s.Mock.Called(key, stopCh)
	return args.Get(0).(<-chan *store.KVPair), args.Error(1)
}

// WatchTree mock
func (s *Mock) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
	args := s.Mock.Called(prefix, stopCh)
	return args.Get(0).(chan []*store.KVPair), args.Error(1)
}

// NewLock mock
func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
	args := s.Mock.Called(key, options)
	return args.Get(0).(store.Locker), args.Error(1)
}

// List mock
func (s *Mock) List(prefix string) ([]*store.KVPair, error) {
	args := s.Mock.Called(prefix)
	return args.Get(0).([]*store.KVPair), args.Error(1)
}

// DeleteTree mock
func (s *Mock) DeleteTree(prefix string) error {
	args := s.Mock.Called(prefix)
	return args.Error(0)
}

// AtomicPut mock
func (s *Mock) AtomicPut(key string, value []byte, previous *store.KVPair, opts *store.WriteOptions) (bool, *store.KVPair, error) {
	args := s.Mock.Called(key, value, previous, opts)
	return args.Bool(0), args.Get(1).(*store.KVPair), args.Error(2)
}

// AtomicDelete mock
func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
	args := s.Mock.Called(key, previous)
	return args.Bool(0), args.Error(1)
}

// Lock mock implementation of Locker
type Lock struct {
	mock.Mock
}

// Lock mock
func (l *Lock) Lock(stopCh chan struct{}) (<-chan struct{}, error) {
	args := l.Mock.Called(stopCh)
	return args.Get(0).(<-chan struct{}), args.Error(1)
}

// Unlock mock
func (l *Lock) Unlock() error {
	args := l.Mock.Called()
	return args.Error(0)
}

// Close mock
func (s *Mock) Close() {
	return
}
<commit_msg>fix import path<commit_after>package mock

import (
	"github.com/YuleiXiao/kvstore"
	"github.com/stretchr/testify/mock"
)

// Mock store. Mocks all Store functions using testify.Mock
type Mock struct {
	mock.Mock

	// Endpoints passed to InitializeMock
	Endpoints []string

	// Options passed to InitializeMock
	Options *store.Config
}

// New creates a Mock store
func New(endpoints []string, options *store.Config) (store.Store, error) {
	s := &Mock{}
	s.Endpoints = endpoints
	s.Options = options
	return s, nil
}

// Put mock
func (s *Mock) Put(key string, value []byte, opts *store.WriteOptions) error {
	args := s.Mock.Called(key, value, opts)
	return args.Error(0)
}

// Get mock
func (s *Mock) Get(key string) (*store.KVPair, error) {
	args := s.Mock.Called(key)
	return args.Get(0).(*store.KVPair), args.Error(1)
}

// Delete mock
func (s *Mock) Delete(key string) error {
	args := s.Mock.Called(key)
	return args.Error(0)
}

// Exists mock
func (s *Mock) Exists(key string) (bool, error) {
	args := s.Mock.Called(key)
	return args.Bool(0), args.Error(1)
}

// Watch mock
func (s *Mock) Watch(key string, stopCh <-chan struct{}) (<-chan *store.KVPair, error) {
	args := s.Mock.Called(key, stopCh)
	return args.Get(0).(<-chan *store.KVPair), args.Error(1)
}

// WatchTree mock
func (s *Mock) WatchTree(prefix string, stopCh <-chan struct{}) (<-chan []*store.KVPair, error) {
	args := s.Mock.Called(prefix, stopCh)
	return args.Get(0).(chan []*store.KVPair), args.Error(1)
}

// NewLock mock
func (s *Mock) NewLock(key string, options *store.LockOptions) (store.Locker, error) {
	args := s.Mock.Called(key, options)
	return args.Get(0).(store.Locker), args.Error(1)
}

// List mock
func (s *Mock) List(prefix string) ([]*store.KVPair, error) {
	args := s.Mock.Called(prefix)
	return args.Get(0).([]*store.KVPair), args.Error(1)
}

// DeleteTree mock
func (s *Mock) DeleteTree(prefix string) error {
	args := s.Mock.Called(prefix)
	return args.Error(0)
}

// AtomicPut mock
func (s *Mock) AtomicPut(key string, value []byte, previous *store.KVPair, opts *store.WriteOptions) (bool, *store.KVPair, error) {
	args := s.Mock.Called(key, value, previous, opts)
	return args.Bool(0), args.Get(1).(*store.KVPair), args.Error(2)
}

// AtomicDelete mock
func (s *Mock) AtomicDelete(key string, previous *store.KVPair) (bool, error) {
	args := s.Mock.Called(key, previous)
	return args.Bool(0), args.Error(1)
}

// Lock mock implementation of Locker
type Lock struct {
	mock.Mock
}

// Lock mock
func (l *Lock) Lock(stopCh chan struct{}) (<-chan struct{}, error) {
	args := l.Mock.Called(stopCh)
	return args.Get(0).(<-chan struct{}), args.Error(1)
}

// Unlock mock
func (l *Lock) Unlock() error {
	args := l.Mock.Called()
	return args.Error(0)
}

// Close mock
func (s *Mock) Close() {
	return
}
<|endoftext|>


<commit_before>// 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 main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/NoahShen/go-simsimi"
)

var bot *linebot.Client
var session *simsimi.SimSimiSession
func main() {
	var err error
	session, _ = simsimi.CreateSimSimiSession("Wallte")
	bot, err = linebot.New(os.Getenv("CHANNEL_SECRET"), os.Getenv("CHANNEL_TOKEN"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				/*if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.ID+":"+message.Text+" OK!")).Do(); err != nil {
					log.Print(err)
				}*/
				imageURL := "https://github.com/AdityaMili95/Wallte/blob/master/README/qI5Ujdy9n1"
				template := linebot.NewButtonsTemplate(
					imageURL, "My button sample"+message.Text, "Hello, my button",
					linebot.NewURITemplateAction("Go to line.me", "https://line.me"),
					linebot.NewPostbackTemplateAction("Say hello1", "hello こんにちは", ""),
					linebot.NewPostbackTemplateAction("言 hello2", "hello こんにちは", "hello こんにちは"),
					linebot.NewMessageTemplateAction("Say message", "Rice=米"),
				)
				if _, err := bot.ReplyMessage(
					event.ReplyToken,
					linebot.NewTemplateMessage("Buttons alt text", template),
				).Do(); err != nil {
					return
				}

				responseText, _ := session.Talk(message.Text)
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(responseText)).Do(); err != nil {
					log.Print(err)
				}

			}
		}else if event.Type == linebot.EventTypePostback{
			if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("iniPostback")).Do(); err != nil {
					log.Print(err)
				}
		}

	}
}
<commit_msg>add carousel<commit_after>// 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 main

import (
	"fmt"
	"log"
	"net/http"
	"os"

	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/NoahShen/go-simsimi"
)

var bot *linebot.Client
var session *simsimi.SimSimiSession
func main() {
	var err error
	session, _ = simsimi.CreateSimSimiSession("Wallte")
	bot, err = linebot.New(os.Getenv("CHANNEL_SECRET"), os.Getenv("CHANNEL_TOKEN"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)
}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				/*if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(message.ID+":"+message.Text+" OK!")).Do(); err != nil {
					log.Print(err)
				}*/
				imageURL := "https://github.com/AdityaMili95/Wallte/blob/master/README/qI5Ujdy9n1"
				template := linebot.NewButtonsTemplate(
					imageURL, "My button sample"+message.Text, "Hello, my button",
					linebot.NewURITemplateAction("Go to line.me", "https://line.me"),
					linebot.NewPostbackTemplateAction("Say hello1", "hello こんにちは", ""),
					linebot.NewPostbackTemplateAction("言 hello2", "hello こんにちは", "hello こんにちは"),
					linebot.NewMessageTemplateAction("Say message", "Rice=米"),
				)
				if _, err := bot.ReplyMessage(
					event.ReplyToken,
					linebot.NewTemplateMessage("Buttons alt text", template),
				).Do(); err != nil {
					return
				}

				responseText, _ := session.Talk(message.Text)
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(responseText)).Do(); err != nil {
					log.Print(err)
				}

			}
		}else if event.Type == linebot.EventTypePostback{
			if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage("iniPostback")).Do(); err != nil {
					log.Print(err)
				}

			template := linebot.NewCarouselTemplate(
				linebot.NewCarouselColumn(
					imageURL, "hoge", "fuga",
					linebot.NewURITemplateAction("Go to line.me", "https://line.me"),
					linebot.NewPostbackTemplateAction("Say hello1", "hello こんにちは", ""),
				),
				linebot.NewCarouselColumn(
					imageURL, "hoge", "fuga",
					linebot.NewPostbackTemplateAction("言 hello2", "hello こんにちは", "hello こんにちは"),
					linebot.NewMessageTemplateAction("Say message", "Rice=米"),
				),
			)
			if _, err := app.bot.ReplyMessage(
				replyToken,
				linebot.NewTemplateMessage("Carousel alt text", template),
			).Do(); err != nil {
				return err
			}
		}

	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"runtime/debug"
	"strings"
	"time"
)

type Fortune struct {
	wr   http.ResponseWriter
	rq   *http.Request
	deck *Deck
}

func init() {
	debug := flag.Bool("d", false, "debug")
	flag.Parse()

	if !*debug {
		log.SetOutput(ioutil.Discard)
	}
}

func main() {
	var fortune Fortune

	fmt.Println("Listening on http://localhost:8080")

	err := http.ListenAndServe(":8080", &fortune)
	if err != nil {
		fmt.Println(err)
	}
}

func (f *Fortune) ServeHTTP(wr http.ResponseWriter, rq *http.Request) {
	defer func() {
		obj := recover()
		if obj != nil {
			msg := fmt.Sprintf("
Error: %v\nStack: %v
", obj, string(debug.Stack()))
			io.WriteString(wr, msg)
			fmt.Println(msg)
		}
	}()

	f.wr = wr
	f.rq = rq

	path := rq.URL.Path
	contentType := "text/html"
	root := "."

	switch {
	case strings.HasPrefix(path, "/playing-cards/"):
		contentType = "image/png"
		wr.Header().Set("cache-control", "public, max-age=0")

	case strings.HasPrefix(path, "/js/"):
		contentType = "application/javascript"

	case strings.HasPrefix(path, "/css/"):
		contentType = "text/css"

	case strings.HasPrefix(path, "/html/"):
		contentType = "text/html"

	case path == "/":
		contentType = "text/html"
		path = "/html/main.html"
		fmt.Printf("%s: Visitor from %s\n", time.Now(), rq.RemoteAddr)

	case path == "/init":
		f.init()
		path = ""

	case path == "/deal":
		f.deal()
		path = ""

	case path == "/fortune":
		f.fortune()
		path = ""
	}

	if len(path) > 0 {
		wr.Header().Set("Content-Type", contentType)
		data, err := ioutil.ReadFile(root + path)
		if err == nil {
			wr.Write(data)
		} else {
			fmt.Fprint(wr, err)
		}
	}
}

func (f *Fortune) init() {
	f.deck = &Deck{}
	f.deck.init()
	f.deck.shuffle()
	f.deck.Cards = f.deck.Cards[:21]

	type Response struct {
		Cards []*Card
		Error string
	}

	response := &Response{
		Cards: f.deck.Cards,
	}
	data, err := json.Marshal(response)
	if err != nil {
		response.Error = err.Error()
	}
	f.wr.Header().Set("Content-Type", "application/json")
	f.wr.Write(data)
}

func (f *Fortune) deal() {
	type RequestCard struct {
		Image string
	}
	type Request struct {
		Cards []RequestCard
		Row   int
		Count int
	}
	type Response struct {
		Row1  []*Card
		Row2  []*Card
		Row3  []*Card
		Card  string
		Error string
	}

	response := &Response{}
	reqData, err := ioutil.ReadAll(f.rq.Body)
	if err != nil {
		response.Error = err.Error()
	} else {
		request := &Request{}
		err = json.Unmarshal(reqData, request)
		if err != nil {
			response.Error = err.Error()
		}
		f.deck = &Deck{}
		for _, card := range request.Cards {
			f.deck.Cards = append(f.deck.Cards, &Card{Image: card.Image})
		}
		if len(request.Cards) == 21 {
			if request.Row == 0 {
				response.Row1 = f.deck.Cards[:7]
				response.Row2 = f.deck.Cards[7:14]
				response.Row3 = f.deck.Cards[14:]
			} else {
				f.deck.placeMiddle(request.Row)
				f.deck.deal()
				response.Row1 = f.deck.Row1
				response.Row2 = f.deck.Row2
				response.Row3 = f.deck.Row3
			}
		} else {
			response.Error += "\nDeck should have 21 cards."
		}
		log.Printf("request: %v\n", request)
		if request.Count == 3 {
			response.Card = f.deck.Row2[3].Image
			log.Printf("memorized card: %s\n", response.Card)
		}
	}

	data, err := json.Marshal(response)
	if err != nil {
		response.Error = err.Error()
	}
	f.wr.Header().Set("Content-Type", "application/json")
	f.wr.Write(data)
}

func (f *Fortune) fortune() {
	words := map[string]string{
		"2C.png":  "law",
		"2D.png":  "wealth",
		"2H.png":  "love",
		"2S.png":  "passion",
		"3C.png":  "rule",
		"3D.png":  "rich",
		"3H.png":  "like",
		"3S.png":  "interest",
		"4C.png":  "command",
		"4D.png":  "gold",
		"4H.png":  "nice",
		"4S.png":  "positive",
		"5C.png":  "advise",
		"5D.png":  "money",
		"5H.png":  "related",
		"5S.png":  "real",
		"6C.png":  "statement",
		"6D.png":  "fortune",
		"6H.png":  "good",
		"6S.png":  "growing",
		"7C.png":  "court",
		"7D.png":  "well",
		"7H.png":  "sweet",
		"7S.png":  "study",
		"8C.png":  "action",
		"8D.png":  "cash",
		"8H.png":  "protect",
		"8S.png":  "understand",
		"9C.png":  "act",
		"9D.png":  "stock",
		"9H.png":  "live",
		"9S.png":  "hobby",
		"10C.png": "order",
		"10D.png": "value",
		"10H.png": "friend",
		"10S.png": "knowledge",
		"JC.png":  "judge",
		"JD.png":  "banker",
		"JH.png":  "husband",
		"JS.png":  "student",
		"QC.png":  "queen",
		"QD.png":  "actress",
		"QH.png":  "wife",
		"QS.png":  "nurse",
		"KC.png":  "congressman",
		"KD.png":  "ceo",
		"KH.png":  "lover",
		"KS.png":  "researcher",
		"AC.png":  "country",
		"AD.png":  "thesaurus",
		"AH.png":  "family",
		"AS.png":  "president",
	}
	type Request struct {
		Card string
	}
	type Response struct {
		Tweet string
		Error string
	}

	response := &Response{}

	request := &Request{}
	reqData, err := ioutil.ReadAll(f.rq.Body)
	err = json.Unmarshal(reqData, request)
	if err != nil {
		response.Error = err.Error()
	}

	key, _ := words[request.Card]
	url := "https://twitter.com/search?q=" + key
	resp, err := http.Get(url)
	if err != nil {
		response.Error = "Error: " + err.Error()
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		response.Error = "Error: " + err.Error()
	}
	resp.Body.Close()

	search := regexp.MustCompile(`<p class="TweetTextSize .*>.*</p>`)
	tweets := search.FindStringSubmatch(string(body))

	tweet := "Unable to fetch tweets."
	if len(tweets) > 0 {
		tweet = tweets[0]
	}
	response.Tweet = tweet
	fmt.Printf("Visitor=%s word=%s fortune=%s\n", f.rq.RemoteAddr, key, tweet)

	data, err := json.Marshal(response)
	if err != nil {
		response.Error = err.Error()
	}
	f.wr.Header().Set("Content-Type", "application/json")
	f.wr.Write(data)
}
<commit_msg>changed cache-control<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"runtime/debug"
	"strings"
	"time"
)

type Fortune struct {
	wr   http.ResponseWriter
	rq   *http.Request
	deck *Deck
}

func init() {
	debug := flag.Bool("d", false, "debug")
	flag.Parse()

	if !*debug {
		log.SetOutput(ioutil.Discard)
	}
}

func main() {
	var fortune Fortune

	fmt.Println("Listening on http://localhost:8080")

	err := http.ListenAndServe(":8080", &fortune)
	if err != nil {
		fmt.Println(err)
	}
}

func (f *Fortune) ServeHTTP(wr http.ResponseWriter, rq *http.Request) {
	defer func() {
		obj := recover()
		if obj != nil {
			msg := fmt.Sprintf("
Error: %v\nStack: %v
", obj, string(debug.Stack()))
			io.WriteString(wr, msg)
			fmt.Println(msg)
		}
	}()

	f.wr = wr
	f.rq = rq

	path := rq.URL.Path
	contentType := "text/html"
	root := "."

	switch {
	case strings.HasPrefix(path, "/playing-cards/"):
		contentType = "image/png"
		wr.Header().Set("cache-control", "public")

	case strings.HasPrefix(path, "/js/"):
		contentType = "application/javascript"

	case strings.HasPrefix(path, "/css/"):
		contentType = "text/css"

	case strings.HasPrefix(path, "/html/"):
		contentType = "text/html"

	case path == "/":
		contentType = "text/html"
		path = "/html/main.html"
		fmt.Printf("%s: Visitor from %s\n", time.Now(), rq.RemoteAddr)

	case path == "/init":
		f.init()
		path = ""

	case path == "/deal":
		f.deal()
		path = ""

	case path == "/fortune":
		f.fortune()
		path = ""
	}

	if len(path) > 0 {
		wr.Header().Set("Content-Type", contentType)
		data, err := ioutil.ReadFile(root + path)
		if err == nil {
			wr.Write(data)
		} else {
			fmt.Fprint(wr, err)
		}
	}
}

func (f *Fortune) init() {
	f.deck = &Deck{}
	f.deck.init()
	f.deck.shuffle()
	f.deck.Cards = f.deck.Cards[:21]

	type Response struct {
		Cards []*Card
		Error string
	}

	response := &Response{
		Cards: f.deck.Cards,
	}
	data, err := json.Marshal(response)
	if err != nil {
		response.Error = err.Error()
	}
	f.wr.Header().Set("Content-Type", "application/json")
	f.wr.Write(data)
}

func (f *Fortune) deal() {
	type RequestCard struct {
		Image string
	}
	type Request struct {
		Cards []RequestCard
		Row   int
		Count int
	}
	type Response struct {
		Row1  []*Card
		Row2  []*Card
		Row3  []*Card
		Card  string
		Error string
	}

	response := &Response{}
	reqData, err := ioutil.ReadAll(f.rq.Body)
	if err != nil {
		response.Error = err.Error()
	} else {
		request := &Request{}
		err = json.Unmarshal(reqData, request)
		if err != nil {
			response.Error = err.Error()
		}
		f.deck = &Deck{}
		for _, card := range request.Cards {
			f.deck.Cards = append(f.deck.Cards, &Card{Image: card.Image})
		}
		if len(request.Cards) == 21 {
			if request.Row == 0 {
				response.Row1 = f.deck.Cards[:7]
				response.Row2 = f.deck.Cards[7:14]
				response.Row3 = f.deck.Cards[14:]
			} else {
				f.deck.placeMiddle(request.Row)
				f.deck.deal()
				response.Row1 = f.deck.Row1
				response.Row2 = f.deck.Row2
				response.Row3 = f.deck.Row3
			}
		} else {
			response.Error += "\nDeck should have 21 cards."
		}
		log.Printf("request: %v\n", request)
		if request.Count == 3 {
			response.Card = f.deck.Row2[3].Image
			log.Printf("memorized card: %s\n", response.Card)
		}
	}

	data, err := json.Marshal(response)
	if err != nil {
		response.Error = err.Error()
	}
	f.wr.Header().Set("Content-Type", "application/json")
	f.wr.Write(data)
}

func (f *Fortune) fortune() {
	words := map[string]string{
		"2C.png":  "law",
		"2D.png":  "wealth",
		"2H.png":  "love",
		"2S.png":  "passion",
		"3C.png":  "rule",
		"3D.png":  "rich",
		"3H.png":  "like",
		"3S.png":  "interest",
		"4C.png":  "command",
		"4D.png":  "gold",
		"4H.png":  "nice",
		"4S.png":  "positive",
		"5C.png":  "advise",
		"5D.png":  "money",
		"5H.png":  "related",
		"5S.png":  "real",
		"6C.png":  "statement",
		"6D.png":  "fortune",
		"6H.png":  "good",
		"6S.png":  "growing",
		"7C.png":  "court",
		"7D.png":  "well",
		"7H.png":  "sweet",
		"7S.png":  "study",
		"8C.png":  "action",
		"8D.png":  "cash",
		"8H.png":  "protect",
		"8S.png":  "understand",
		"9C.png":  "act",
		"9D.png":  "stock",
		"9H.png":  "live",
		"9S.png":  "hobby",
		"10C.png": "order",
		"10D.png": "value",
		"10H.png": "friend",
		"10S.png": "knowledge",
		"JC.png":  "judge",
		"JD.png":  "banker",
		"JH.png":  "husband",
		"JS.png":  "student",
		"QC.png":  "queen",
		"QD.png":  "actress",
		"QH.png":  "wife",
		"QS.png":  "nurse",
		"KC.png":  "congressman",
		"KD.png":  "ceo",
		"KH.png":  "lover",
		"KS.png":  "researcher",
		"AC.png":  "country",
		"AD.png":  "thesaurus",
		"AH.png":  "family",
		"AS.png":  "president",
	}
	type Request struct {
		Card string
	}
	type Response struct {
		Tweet string
		Error string
	}

	response := &Response{}

	request := &Request{}
	reqData, err := ioutil.ReadAll(f.rq.Body)
	err = json.Unmarshal(reqData, request)
	if err != nil {
		response.Error = err.Error()
	}

	key, _ := words[request.Card]
	url := "https://twitter.com/search?q=" + key
	resp, err := http.Get(url)
	if err != nil {
		response.Error = "Error: " + err.Error()
	}
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		response.Error = "Error: " + err.Error()
	}
	resp.Body.Close()

	search := regexp.MustCompile(`<p class="TweetTextSize .*>.*</p>`)
	tweets := search.FindStringSubmatch(string(body))

	tweet := "Unable to fetch tweets."
	if len(tweets) > 0 {
		tweet = tweets[0]
	}
	response.Tweet = tweet
	fmt.Printf("Visitor=%s word=%s fortune=%s\n", f.rq.RemoteAddr, key, tweet)

	data, err := json.Marshal(response)
	if err != nil {
		response.Error = err.Error()
	}
	f.wr.Header().Set("Content-Type", "application/json")
	f.wr.Write(data)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"os"

	"github.com/anachronistic/apns"

	"github.com/oursky/ourd/authtoken"
	"github.com/oursky/ourd/handler"
	"github.com/oursky/ourd/oddb"
	_ "github.com/oursky/ourd/oddb/fs"
	_ "github.com/oursky/ourd/oddb/pq"
	"github.com/oursky/ourd/push"
	"github.com/oursky/ourd/router"
	"github.com/oursky/ourd/subscription"
)

func usage() {
	fmt.Println("Usage: ourd [<config file>]")
}

func main() {
	var configPath string
	if len(os.Args) < 2 {
		configPath = os.Getenv("OD_CONFIG")
		if configPath == "" {
			usage()
			return
		}
	} else {
		configPath = os.Args[1]
	}

	config := Configuration{}
	if err := ReadFileInto(&config, configPath); err != nil {
		fmt.Println(err.Error())
		return
	}

	if config.Subscription.Enabled {
		pushSender := &push.APNSPusher{
			Client: apns.NewClient(config.APNS.Gateway, config.APNS.CertPath, config.APNS.KeyPath),
		}
		subscriptionService := &subscription.Service{
			ConnOpener:         func() (oddb.Conn, error) { return oddb.Open(config.DB.ImplName, config.App.Name, config.DB.Option) },
			NotificationSender: pushSender,
		}
		subscriptionService.Init()
	}

	// Setup Logging
	log.SetOutput(os.Stderr)
	logLv, logE := log.ParseLevel(config.LOG.Level)
	if logE != nil {
		logLv = log.DebugLevel
	}
	log.SetLevel(logLv)

	naiveAPIKeyPreprocessor := apiKeyValidatonPreprocessor{
		Key:     config.App.APIKey,
		AppName: config.App.Name,
	}

	fileTokenStorePreprocessor := tokenStorePreprocessor{
		Store: authtoken.FileStore(config.TokenStore.Path).Init(),
	}

	authenticator := userAuthenticator{
		APIKey:  config.App.APIKey,
		AppName: config.App.Name,
	}

	fileSystemConnPreprocessor := connPreprocessor{
		DBOpener: oddb.Open,
		DBImpl:   config.DB.ImplName,
		Option:   config.DB.Option,
	}

	r := router.NewRouter()
	r.Map("", handler.HomeHandler)

	authPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
		fileSystemConnPreprocessor.Preprocess,
		fileTokenStorePreprocessor.Preprocess,
	}
	r.Map("auth:signup", handler.SignupHandler, authPreprocessors...)
	r.Map("auth:login", handler.LoginHandler, authPreprocessors...)

	recordPreprocessors := []router.Processor{
		fileTokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		fileSystemConnPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
	}
	r.Map("record:fetch", handler.RecordFetchHandler, recordPreprocessors...)
	r.Map("record:query", handler.RecordQueryHandler, recordPreprocessors...)
	r.Map("record:save", handler.RecordSaveHandler, recordPreprocessors...)
	r.Map("record:delete", handler.RecordDeleteHandler, recordPreprocessors...)

	r.Map("device:register",
		handler.DeviceRegisterHandler,
		fileTokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		fileSystemConnPreprocessor.Preprocess,
		injectUserIfPresent,
	)

	// subscription shares the same set of preprocessor as record at the moment
	r.Map("subscription:save", handler.SubscriptionSaveHandler, recordPreprocessors...)

	log.Printf("Listening on %v...", config.HTTP.Host)
	err := http.ListenAndServe(config.HTTP.Host, r)
	if err != nil {
		log.Printf("Failed: %v", err)
	}
}
<commit_msg>stamp version 1.0.27<commit_after>package main

import (
	"bufio"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"net/http"
	"os"
	"strings"

	"github.com/anachronistic/apns"

	"github.com/oursky/ourd/authtoken"
	"github.com/oursky/ourd/handler"
	"github.com/oursky/ourd/oddb"
	_ "github.com/oursky/ourd/oddb/fs"
	_ "github.com/oursky/ourd/oddb/pq"
	"github.com/oursky/ourd/push"
	"github.com/oursky/ourd/router"
	"github.com/oursky/ourd/subscription"
)

func logMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

		body, _ := ioutil.ReadAll(r.Body)
		log.Debugf("------ Request: ------\n%v\n", body)
		r.Body = bufio.NewReader(body)

		next.ServeHTTP(w, r)
		// log.Debugf("------ Response: ------\n%v\n", w)
	})
}

func usage() {
	fmt.Println("Usage: ourd [<config file>]")
}

func main() {
	var configPath string
	if len(os.Args) < 2 {
		configPath = os.Getenv("OD_CONFIG")
		if configPath == "" {
			usage()
			return
		}
	} else {
		configPath = os.Args[1]
	}

	config := Configuration{}
	if err := ReadFileInto(&config, configPath); err != nil {
		fmt.Println(err.Error())
		return
	}

	if config.Subscription.Enabled {
		pushSender := &push.APNSPusher{
			Client: apns.NewClient(config.APNS.Gateway, config.APNS.CertPath, config.APNS.KeyPath),
		}
		subscriptionService := &subscription.Service{
			ConnOpener:         func() (oddb.Conn, error) { return oddb.Open(config.DB.ImplName, config.App.Name, config.DB.Option) },
			NotificationSender: pushSender,
		}
		subscriptionService.Init()
	}

	// Setup Logging
	log.SetOutput(os.Stderr)
	logLv, logE := log.ParseLevel(config.LOG.Level)
	if logE != nil {
		logLv = log.DebugLevel
	}
	log.SetLevel(logLv)

	naiveAPIKeyPreprocessor := apiKeyValidatonPreprocessor{
		Key:     config.App.APIKey,
		AppName: config.App.Name,
	}

	fileTokenStorePreprocessor := tokenStorePreprocessor{
		Store: authtoken.FileStore(config.TokenStore.Path).Init(),
	}

	authenticator := userAuthenticator{
		APIKey:  config.App.APIKey,
		AppName: config.App.Name,
	}

	fileSystemConnPreprocessor := connPreprocessor{
		DBOpener: oddb.Open,
		DBImpl:   config.DB.ImplName,
		Option:   config.DB.Option,
	}

	r := router.NewRouter()
	r.Map("", handler.HomeHandler)

	authPreprocessors := []router.Processor{
		naiveAPIKeyPreprocessor.Preprocess,
		fileSystemConnPreprocessor.Preprocess,
		fileTokenStorePreprocessor.Preprocess,
	}
	r.Map("auth:signup", handler.SignupHandler, authPreprocessors...)
	r.Map("auth:login", handler.LoginHandler, authPreprocessors...)

	recordPreprocessors := []router.Processor{
		fileTokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		fileSystemConnPreprocessor.Preprocess,
		injectUserIfPresent,
		injectDatabase,
	}
	r.Map("record:fetch", handler.RecordFetchHandler, recordPreprocessors...)
	r.Map("record:query", handler.RecordQueryHandler, recordPreprocessors...)
	r.Map("record:save", handler.RecordSaveHandler, recordPreprocessors...)
	r.Map("record:delete", handler.RecordDeleteHandler, recordPreprocessors...)

	r.Map("device:register",
		handler.DeviceRegisterHandler,
		fileTokenStorePreprocessor.Preprocess,
		authenticator.Preprocess,
		fileSystemConnPreprocessor.Preprocess,
		injectUserIfPresent,
	)

	// subscription shares the same set of preprocessor as record at the moment
	r.Map("subscription:save", handler.SubscriptionSaveHandler, recordPreprocessors...)

	log.Printf("Listening on %v...", config.HTTP.Host)
	err := http.ListenAndServe(config.HTTP.Host, logMiddleware(r))
	if err != nil {
		log.Printf("Failed: %v", err)
	}
}
<|endoftext|>


<commit_before>//
// Copyright: 2016, mgIT GmbH <office@mgit.at>
// All rights reserved.
//
// 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 main

import (
	"flag"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"syscall"
	"time"

	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

const (
	LOCK_FILE_BASE = "/singleton.mgit.at/"
)

func runChild(cmd string, args []string, signals <-chan os.Signal) (err error) {
	child := exec.Cmd{}
	child.Path = cmd
	child.Args = args
	child.Stdin = os.Stdin
	child.Stdout = os.Stdout
	child.Stderr = os.Stderr

	if err = child.Start(); err != nil {
		return err
	}

	go func() {
		for sig := range signals {
			child.Process.Signal(sig)
		}
	}()
	return child.Wait()
}

func initETCdClient(updateTimeout time.Duration) error {
	cfg := client.Config{
		Endpoints:               []string{"http://127.0.0.1:2379"},
		Transport:               client.DefaultTransport,
		HeaderTimeoutPerRequest: updateTimeout,
	}
	c, err := client.New(cfg)
	if err != nil {
		return err
	}
	kapi := client.NewKeysAPI(c)
	if resp, err := kapi.Set(context.Background(), "/foo", "bar", nil); err != nil {
		log.Fatal(err)
	} else {
		log.Printf("Set is done. Metadata is %q\n", resp)
	}
	return nil
}

func acquireLock(lockfile string, ttl time.Duration) (err error) {
	log.Printf("trying to acquire lock: %s (TTL: %v)", lockfile, ttl)
	// TODO: try to create lock file, if it exists attach to it and wait for updated/deleted event
	// when updated exit
	// when deleted try again
	return
}

func updateLock(lockfile string, ttl, timeout time.Duration) (err error) {
	log.Printf("trying to update lock: %s (TTL: %v, Timeout: %v)", lockfile, ttl, timeout)
	// Try to update the lock: return err if this fails
	return
}

func main() {
	var nameTemplate = flag.String("name-template", "", "template for the lockfile name (will get expanded using environment variables)")
	var updateInterval = flag.Uint("update-interval", 30, "interval in seconds between lock file update requests")
	var updateTimeout = flag.Uint("update-timeout", 5, "timeout in seconds to wait for response from etcd")
	var gracePeriod = flag.Uint("grace-period", 30, "time in seconds to wait for a normal shutdown of the child")
	var killDelay = flag.Uint("kill-delay", 5, "")

	flag.Parse()

	if *nameTemplate == "" {
		log.Fatal("singleton-runner: '-name-template' is empty")
	}
	name := os.ExpandEnv(*nameTemplate)
	lockfilePath := filepath.Join(LOCK_FILE_BASE, path.Clean("/"+name))

	var cmd string
	args := flag.Args()
	if args := flag.Args(); len(args) > 0 {
		cmd = args[0]
	} else {
		log.Fatal("singleton-runner: please specify a command to run")
	}

	ttl := time.Duration(*updateInterval+*updateTimeout+*gracePeriod+*killDelay) * time.Second

	if err := initETCdClient(time.Duration(*updateTimeout) * time.Second); err != nil {
		log.Fatal("error connecting to etcd:", err)
	}

	if err := acquireLock(lockfilePath, ttl); err != nil {
		log.Fatal("singleton-runner: unable to acquier lock:", err)
	}
	exited := make(chan bool, 1)

	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM)
	go func() {
		defer func() {
			exited <- true
		}()
		err := runChild(cmd, args, signals)
		if err != nil {
			log.Printf("singleton-runner: child exited with: %v", err)
		} else {
			log.Printf("singleton-runner: child exited normally")
		}
	}()

	t := time.NewTicker(time.Duration(*updateInterval) * time.Second)
	for {
		select {
		case <-t.C:
			if err := updateLock(lockfilePath, ttl, time.Duration(*updateTimeout)*time.Second); err != nil {
				log.Println("singleton-runner: updateting lock failed:", err)
				// send TERM signal to client
				// if after gracePeriod child is still running send a KILL signal
			}
		case <-exited:
			log.Println("singleton-runner: closing...")
			return
		}
	}
}
<commit_msg>acqureing lock works now<commit_after>//
// Copyright: 2016, mgIT GmbH <office@mgit.at>
// All rights reserved.
//
// 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 main

import (
	"errors"
	"flag"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"path/filepath"
	"syscall"
	"time"

	"github.com/coreos/etcd/client"
	"golang.org/x/net/context"
)

const (
	LOCK_FILE_BASE = "/singleton.mgit.at"
)

func runChild(cmd string, args []string, signals <-chan os.Signal) (err error) {
	child := exec.Cmd{}
	child.Path = cmd
	child.Args = args
	child.Stdin = os.Stdin
	child.Stdout = os.Stdout
	child.Stderr = os.Stderr

	if err = child.Start(); err != nil {
		return err
	}

	go func() {
		for sig := range signals {
			child.Process.Signal(sig)
		}
	}()
	return child.Wait()
}

func initETCdClient(updateTimeout time.Duration) (client.KeysAPI, error) {
	cfg := client.Config{
		Endpoints:               []string{"http://127.0.0.1:2379"}, // TODO: make this configurable
		Transport:               client.DefaultTransport,
		HeaderTimeoutPerRequest: updateTimeout,
	}
	c, err := client.New(cfg)
	if err != nil {
		return nil, err
	}
	kapi := client.NewKeysAPI(c)
	return kapi, err
}

func IsKeyExists(err error) bool {
	if cErr, ok := err.(client.Error); ok {
		return cErr.Code == client.ErrorCodeNodeExist
	}
	return false
}

func acquireLock(kapi client.KeysAPI, lockfile string, ttl time.Duration) (err error) {
	log.Printf("singleton-runner: trying to acquire lock: %s (TTL: %v)", lockfile, ttl)

	opts := &client.SetOptions{PrevExist: client.PrevNoExist, TTL: ttl, Refresh: false}
	if _, err = kapi.Set(context.Background(), lockfile, "kubernetes-pod-id", opts); err == nil { // TODO: use kubernetes pod name as value...
		return
	}
	if !IsKeyExists(err) {
		return
	}
	log.Printf("singleton-runner: lock is already acquired - watching for changes")
	err = errors.New("watching lockfile not yet implemented!")
	// TODO: attach to lockfile and wait for updated/deleted event
	// when updated exit
	// when deleted try again
	return
}

func releaseLock(kapi client.KeysAPI, lockfile string) {
	log.Printf("trying to release lock: %s", lockfile)
}

func updateLock(kapi client.KeysAPI, lockfile string, ttl, timeout time.Duration) (err error) {
	log.Printf("trying to update lock: %s (TTL: %v, Timeout: %v)", lockfile, ttl, timeout)
	// Try to update the lock: return err if this fails
	return
}

func main() {
	var nameTemplate = flag.String("name-template", "", "template for the lockfile name (will get expanded using environment variables)")
	var updateInterval = flag.Uint("update-interval", 30, "interval in seconds between lock file update requests")
	var updateTimeout = flag.Uint("update-timeout", 5, "timeout in seconds to wait for response from etcd")
	var gracePeriod = flag.Uint("grace-period", 30, "time in seconds to wait for a normal shutdown of the child")
	var killDelay = flag.Uint("kill-delay", 5, "")

	flag.Parse()

	if *nameTemplate == "" {
		log.Fatal("singleton-runner: '-name-template' is empty")
	}
	name := os.ExpandEnv(*nameTemplate)
	lockfilePath := filepath.Join(LOCK_FILE_BASE, path.Clean("/"+name))

	var cmd string
	args := flag.Args()
	if args := flag.Args(); len(args) > 0 {
		cmd = args[0]
	} else {
		log.Fatal("singleton-runner: please specify a command to run")
	}

	ttl := time.Duration(*updateInterval+*updateTimeout+*gracePeriod+*killDelay) * time.Second

	kapi, err := initETCdClient(time.Duration(*updateTimeout) * time.Second)
	if err != nil {
		log.Fatal("error connecting to etcd:", err)
	}
	// defer releaseLock(kapi, lockfilePath) // should this be done in any case?

	if err := acquireLock(kapi, lockfilePath, ttl); err != nil {
		log.Fatal("singleton-runner: unable to acquire lock: ", err)
	}

	log.Printf("singleton-runner: lock acquired successfully! .. starting %q", cmd)
	exited := make(chan bool, 1)

	signals := make(chan os.Signal)
	signal.Notify(signals, syscall.SIGHUP, syscall.SIGTERM)
	go func() {
		defer func() {
			exited <- true
		}()
		err := runChild(cmd, args, signals)
		if err != nil {
			log.Printf("singleton-runner: child exited with: %v", err)
		} else {
			log.Printf("singleton-runner: child exited normally")
		}
	}()

	t := time.NewTicker(time.Duration(*updateInterval) * time.Second)
	for {
		select {
		case <-t.C:
			if err := updateLock(kapi, lockfilePath, ttl, time.Duration(*updateTimeout)*time.Second); err != nil {
				log.Println("singleton-runner: updateting lock failed:", err)
				// send TERM signal to client
				// if after gracePeriod child is still running send a KILL signal
			}
		case <-exited:
			log.Println("singleton-runner: closing...")
			releaseLock(kapi, lockfilePath) // remove here if we do this using the defer above
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"log"
)

var (
	operation = flag.String("op", "none", "Operation to perform on numbers 1 & 2.")
	num1      = flag.Int("num1", 0, "First Number (integer)")
	num2      = flag.Int("num2", 0, "Second Number (integer)")

	operations = map[string]fnOperation{
		"none": NoneOperator,
		"add":  AddOperator,
		"sub":  SubOperator,
		"mul":  MultiOperator,
		"div":  DivOperator,
	}
)

type fnOperation func(num1 int, num2 int) (float64, string, error)

func main() {
	flag.Parse()
	log.Println("FlagFunc - How to use flags with functions and interfaces.")
	if operations[*operation] == nil {
		log.Printf("Valid Values are:\n")
		for key, _ := range operations {
			log.Println(key)
		}
		log.Fatalf("Operation %v does not exist.", *operation)
	} else {
		fnOperator := operations[*operation]
		if result, sign, err := fnOperator(*num1, *num2); err != nil {
			log.Println("Error:", err)
		} else {
			log.Printf("%v %v %v = %v", *num1, sign, *num2, result)
		}
	}

}

func NoneOperator(num1 int, num2 int) (float64, string, error) {
	return 0.0, "", errors.New("No operation was specified!")
}

func AddOperator(num1 int, num2 int) (float64, string, error) {
	return float64(num1 + num2), "+", nil
}

func SubOperator(num1 int, num2 int) (float64, string, error) {
	return float64(num1 - num2), "-", nil
}

func MultiOperator(num1 int, num2 int) (float64, string, error) {
	return float64(num1 * num2), "*", nil
}

func DivOperator(num1 int, num2 int) (float64, string, error) {
	if num2 == 0 {
		return 0.0, "", errors.New("For division (div), num2 cannot equal 0.")
	}
	return float64(num1) / float64(num2), "/", nil
}
<commit_msg>added some helper text to the logs<commit_after>package main

import (
	"errors"
	"flag"
	"log"
)

var (
	operation = flag.String("op", "none", "Operation to perform on numbers 1 & 2.")
	num1      = flag.Int("num1", 0, "First Number (integer)")
	num2      = flag.Int("num2", 0, "Second Number (integer)")

	operations = map[string]fnOperation{
		"none": NoneOperator,
		"add":  AddOperator,
		"sub":  SubOperator,
		"mul":  MultiOperator,
		"div":  DivOperator,
	}
)

type fnOperation func(num1 int, num2 int) (float64, string, error)

func main() {
	flag.Parse()
	log.Println("FlagFunc - How to use flags with functions and interfaces.")
	if operations[*operation] == nil {
		log.Printf("Valid Values are:\n")
		for key, _ := range operations {
			log.Println(key)
		}
		log.Fatalf("Operation %v does not exist.", *operation)
	} else {
		fnOperator := operations[*operation]
		if result, sign, err := fnOperator(*num1, *num2); err != nil {
			log.Println("Error:", err)
			if sign == "none" {
				log.Println("Try these parameters: -num1 5 -num2 10 -op add")
			}
		} else {
			log.Printf("%v %v %v = %v", *num1, sign, *num2, result)
		}
	}

}

func NoneOperator(num1 int, num2 int) (float64, string, error) {
	return 0.0, "none", errors.New("No operation was specified!")
}

func AddOperator(num1 int, num2 int) (float64, string, error) {
	return float64(num1 + num2), "+", nil
}

func SubOperator(num1 int, num2 int) (float64, string, error) {
	return float64(num1 - num2), "-", nil
}

func MultiOperator(num1 int, num2 int) (float64, string, error) {
	return float64(num1 * num2), "*", nil
}

func DivOperator(num1 int, num2 int) (float64, string, error) {
	if num2 == 0 {
		return 0.0, "", errors.New("For division (div), num2 cannot equal 0.")
	}
	return float64(num1) / float64(num2), "/", nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/julienschmidt/httprouter"
	"github.com/oschwald/geoip2-golang"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

// main configuration strct
type configuration struct {
	IP           string `json:"server.ip"`
	Port         int    `json:"server.port"`
	Password     string `json:"server.password"`
	DatabaseFile string `json:"database.file"`
}

// create a new configuration with default values
func newConfiguration() *configuration {
	return &configuration{
		IP:           "127.0.0.1",
		Port:         8000,
		Password:     "",
		DatabaseFile: "",
	}
}

func (c *configuration) loadFromJSONFile(configFile string) {
	currentPath, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	configFilePath := currentPath + string(os.PathSeparator) + configFile

	_, err = os.Stat(configFilePath)
	if err != nil {
		return
	}

	b, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		log.Fatalf("Configuration file read error: %s", err)
	}

	err = json.Unmarshal(b, c)
	if err != nil {
		log.Fatalf("Configuration file marshal error: %s", err)
	}
}

type httpJSONResponse struct {
	Status  string      `json:"status"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

type geoDataResponse struct {
	Continent   string  `json:"continent"`
	CountryName string  `json:"country_name"`
	CountryCode string  `json:"country_code"`
	StateName   string  `json:"state_name"`
	CityName    string  `json:"city_name"`
	PostalCode  string  `json:"postal_code"`
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	TimeZone    string  `json:"timezone"`
}

var (
	config *configuration
	db     *geoip2.Reader
)

func setupHTTP(fn httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		w.Header().Set("Content-Type", "application/json")
		fn(w, r, ps)
	}
}

func sendHTTPJSONResponse(w http.ResponseWriter, status, message string, data interface{}) {
	js, err := json.Marshal(&httpJSONResponse{status, message, data})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(w, string(js))
	return
}

func httpHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if len(config.Password) > 0 && r.Header.Get("Authorization") != config.Password {
		sendHTTPJSONResponse(w, "error", "Invalid password", nil)
		return
	}
	tStart := time.Now()

	ipAddr := ps.ByName("ip")
	ip := net.ParseIP(ipAddr)
	if ip == nil {
		ips, err := net.LookupIP(ipAddr)
		if err != nil {
			sendHTTPJSONResponse(w, "error", "Invalid ip address", nil)
			return
		}
		ip = ips[0]
	}

	if ip == nil {
		sendHTTPJSONResponse(w, "error", "Invalid ip address", nil)
		return
	}

	record, err := db.City(ip)
	if err != nil {
		sendHTTPJSONResponse(w, "error", "Cannot process request", nil)
		return
	}

	res := &geoDataResponse{
		Continent:   record.Continent.Names["en"],
		CountryName: record.Country.Names["en"],
		CountryCode: record.Country.IsoCode,
		StateName:   record.Subdivisions[0].Names["en"],
		CityName:    record.City.Names["en"],
		PostalCode:  record.Postal.Code,
		Latitude:    record.Location.Latitude,
		Longitude:   record.Location.Longitude,
		TimeZone:    record.Location.TimeZone,
	}

	tElapsed := time.Since(tStart)
	sendHTTPJSONResponse(w, "success", fmt.Sprintf("OK [took %s]", tElapsed), res)
}

func aliveHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	if len(config.Password) > 0 && r.Header.Get("Authorization") != config.Password {
		fmt.Fprint(w, "Invalid password")
		return
	}
	fmt.Fprint(w, "pong")
	return
}

func main() {

	defaultConfig := newConfiguration()
	defaultConfig.loadFromJSONFile("config.json")

	ip := flag.String("server.ip", defaultConfig.IP, "server ip address, empty to bind all interfaces")
	port := flag.Int("server.port", defaultConfig.Port, "server port")
	password := flag.String("server.password", defaultConfig.Password, "the password to allow access to the server via http requests")
	dbFile := flag.String("database.file", defaultConfig.DatabaseFile, "the database file that contains GeoIP information")

	flag.Parse()

	config = &configuration{
		IP:           *ip,
		Port:         *port,
		Password:     *password,
		DatabaseFile: *dbFile,
	}

	// no need anymore
	defaultConfig = nil

	// database file
	var err error
	db, err = geoip2.Open(config.DatabaseFile)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	address := fmt.Sprintf("%s:%d", config.IP, config.Port)
	router := httprouter.New()
	router.GET("/ping", aliveHandler)
	router.GET("/check/:ip", setupHTTP(httpHandler))
	log.Fatal(http.ListenAndServe(address, router))
}
<commit_msg>fix index out of range issue<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/julienschmidt/httprouter"
	"github.com/oschwald/geoip2-golang"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

// main configuration strct
type configuration struct {
	IP           string `json:"server.ip"`
	Port         int    `json:"server.port"`
	Password     string `json:"server.password"`
	DatabaseFile string `json:"database.file"`
}

// create a new configuration with default values
func newConfiguration() *configuration {
	return &configuration{
		IP:           "127.0.0.1",
		Port:         8000,
		Password:     "",
		DatabaseFile: "",
	}
}

func (c *configuration) loadFromJSONFile(configFile string) {
	currentPath, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		log.Fatal(err)
	}
	configFilePath := currentPath + string(os.PathSeparator) + configFile

	_, err = os.Stat(configFilePath)
	if err != nil {
		return
	}

	b, err := ioutil.ReadFile(configFilePath)
	if err != nil {
		log.Fatalf("Configuration file read error: %s", err)
	}

	err = json.Unmarshal(b, c)
	if err != nil {
		log.Fatalf("Configuration file marshal error: %s", err)
	}
}

type httpJSONResponse struct {
	Status  string      `json:"status"`
	Message string      `json:"message"`
	Data    interface{} `json:"data"`
}

type geoDataResponse struct {
	Continent   string  `json:"continent"`
	CountryName string  `json:"country_name"`
	CountryCode string  `json:"country_code"`
	StateName   string  `json:"state_name"`
	CityName    string  `json:"city_name"`
	PostalCode  string  `json:"postal_code"`
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	TimeZone    string  `json:"timezone"`
}

var (
	config *configuration
	db     *geoip2.Reader
)

func setupHTTP(fn httprouter.Handle) httprouter.Handle {
	return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		w.Header().Set("Content-Type", "application/json")
		fn(w, r, ps)
	}
}

func sendHTTPJSONResponse(w http.ResponseWriter, status, message string, data interface{}) {
	js, err := json.Marshal(&httpJSONResponse{status, message, data})
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	fmt.Fprint(w, string(js))
	return
}

func httpHandler(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if len(config.Password) > 0 && r.Header.Get("Authorization") != config.Password {
		sendHTTPJSONResponse(w, "error", "Invalid password", nil)
		return
	}
	tStart := time.Now()

	ipAddr := ps.ByName("ip")
	ip := net.ParseIP(ipAddr)
	if ip == nil {
		ips, err := net.LookupIP(ipAddr)
		if err != nil {
			sendHTTPJSONResponse(w, "error", "Invalid ip address", nil)
			return
		}
		ip = ips[0]
	}

	if ip == nil {
		sendHTTPJSONResponse(w, "error", "Invalid ip address", nil)
		return
	}

	record, err := db.City(ip)
	if err != nil {
		sendHTTPJSONResponse(w, "error", "Cannot process request", nil)
		return
	}

	stateName := ""
	if len(record.Subdivisions) > 0 {
		stateName = record.Subdivisions[0].Names["en"]
	}
	res := &geoDataResponse{
		Continent:   record.Continent.Names["en"],
		CountryName: record.Country.Names["en"],
		CountryCode: record.Country.IsoCode,
		StateName:   stateName,
		CityName:    record.City.Names["en"],
		PostalCode:  record.Postal.Code,
		Latitude:    record.Location.Latitude,
		Longitude:   record.Location.Longitude,
		TimeZone:    record.Location.TimeZone,
	}

	tElapsed := time.Since(tStart)
	sendHTTPJSONResponse(w, "success", fmt.Sprintf("OK [took %s]", tElapsed), res)
}

func aliveHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	if len(config.Password) > 0 && r.Header.Get("Authorization") != config.Password {
		fmt.Fprint(w, "Invalid password")
		return
	}
	fmt.Fprint(w, "pong")
	return
}

func main() {

	defaultConfig := newConfiguration()
	defaultConfig.loadFromJSONFile("config.json")

	ip := flag.String("server.ip", defaultConfig.IP, "server ip address, empty to bind all interfaces")
	port := flag.Int("server.port", defaultConfig.Port, "server port")
	password := flag.String("server.password", defaultConfig.Password, "the password to allow access to the server via http requests")
	dbFile := flag.String("database.file", defaultConfig.DatabaseFile, "the database file that contains GeoIP information")

	flag.Parse()

	config = &configuration{
		IP:           *ip,
		Port:         *port,
		Password:     *password,
		DatabaseFile: *dbFile,
	}

	// no need anymore
	defaultConfig = nil

	// database file
	var err error
	db, err = geoip2.Open(config.DatabaseFile)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	address := fmt.Sprintf("%s:%d", config.IP, config.Port)
	router := httprouter.New()
	router.GET("/ping", aliveHandler)
	router.GET("/check/:ip", setupHTTP(httpHandler))
	log.Fatal(http.ListenAndServe(address, router))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/jessevdk/go-flags"
	"log"
	"os"
	"time"
)

type Options struct {
	Verbose			bool	`long:"verbose" short:"v"`

	Interface		string	`long:"interface" short:"i" value-name:"IFACE" description:"Use address from interface"`
	InterfaceFamily	Family	`long:"interface-family"`

	Server		string	`long:"server" value-name:"HOST[:PORT]"`
	Timeout		time.Duration `long:"timeout" value-name:"DURATION" default:"10s"`
	TSIGName	string	`long:"tsig-name"`
	TSIGSecret	string	`long:"tsig-secret" env:"TSIG_SECRET"`
	TSIGAlgorithm TSIGAlgorithm `long:"tsig-algorithm" default:"hmac-sha1."`

	Zone		string	`long:"zone" description:"Zone to update"`
	Name		string	`long:"name" description:"Name to update"`
	TTL			int		`long:"ttl" default:"60"`
}

func main() {
	var options Options

	if args, err := flags.Parse(&options); err != nil {
		log.Fatalf("flags.Parse: %v", err)
		os.Exit(1)
	} else if len(args) > 0 {
		log.Fatalf("Usage: no args")
		os.Exit(1)
	}

	var update = Update{
		ttl:	 options.TTL,
		timeout: options.Timeout,
	}

	if err := update.Init(options.Name, options.Zone, options.Server); err != nil {
		log.Fatalf("init: %v", err)
	}

	if options.TSIGName != "" {
		log.Printf("using TSIG: %v (algo=%v)", options.TSIGName, options.TSIGAlgorithm)

		update.InitTSIG(options.TSIGName, options.TSIGSecret, options.TSIGAlgorithm)
	}

	// addrs
	var addrs = new(AddrSet)

	if options.Interface == "" {

	} else if err := addrs.ScanInterface(options.Interface, options.InterfaceFamily); err != nil {
		log.Fatalf("addrs scan: %v", err)
	}

	// update
	if err := update.Update(addrs, options.Verbose); err != nil {
		log.Fatalf("update: %v", err)
	} else {
		log.Printf("update: ok")
	}
}
<commit_msg>take name as posarg, default for --tsig-name<commit_after>package main

import (
	"github.com/jessevdk/go-flags"
	"log"
	"os"
	"time"
)

type Options struct {
	Verbose			bool	`long:"verbose" short:"v"`

	Interface		string	`long:"interface" short:"i" value-name:"IFACE" description:"Use address from interface"`
	InterfaceFamily	Family	`long:"interface-family"`

	Server		string	`long:"server" value-name:"HOST[:PORT]"`
	Timeout		time.Duration `long:"timeout" value-name:"DURATION" default:"10s"`
	TSIGName	string	`long:"tsig-name"`
	TSIGSecret	string	`long:"tsig-secret" env:"TSIG_SECRET"`
	TSIGAlgorithm TSIGAlgorithm `long:"tsig-algorithm" default:"hmac-sha1."`

	Zone		string	`long:"zone" description:"Zone to update"`
	TTL			int		`long:"ttl" default:"60"`

	Args		struct {
		Name		string	`description:"DNS Name to update"`
	} `positional-args:"yes"`
}

func main() {
	var options Options

	if _, err := flags.Parse(&options); err != nil {
		log.Fatalf("flags.Parse: %v", err)
		os.Exit(1)
	}

	var update = Update{
		ttl:	 options.TTL,
		timeout: options.Timeout,
	}

	if err := update.Init(options.Args.Name, options.Zone, options.Server); err != nil {
		log.Fatalf("init: %v", err)
	}

	if options.TSIGSecret != "" {
		var name = options.TSIGName

		if name == "" {
			name = options.Args.Name
		}

		log.Printf("using TSIG: %v (algo=%v)", name, options.TSIGAlgorithm)

		update.InitTSIG(name, options.TSIGSecret, options.TSIGAlgorithm)
	}

	// addrs
	var addrs = new(AddrSet)

	if options.Interface == "" {

	} else if err := addrs.ScanInterface(options.Interface, options.InterfaceFamily); err != nil {
		log.Fatalf("addrs scan: %v", err)
	}

	// update
	if err := update.Update(addrs, options.Verbose); err != nil {
		log.Fatalf("update: %v", err)
	} else {
		log.Printf("update: ok")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"runtime"
	"strings"

	"github.com/alecthomas/kingpin"
	"github.com/estafette/estafette-ci-contracts"
	crypt "github.com/estafette/estafette-ci-crypt"
	manifest "github.com/estafette/estafette-ci-manifest"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"

	stdlog "log"
)

var (
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()

	secretDecryptionKey = kingpin.Flag("secret-decryption-key", "The AES-256 key used to decrypt secrets that have been encrypted with it.").String()
)

func main() {

	// parse command line parameters
	kingpin.Parse()

	// bootstrap
	secretHelper := crypt.NewSecretHelper(*secretDecryptionKey)
	envvarHelper := NewEnvvarHelper("ESTAFETTE_", secretHelper)
	whenEvaluator := NewWhenEvaluator(envvarHelper)
	dockerRunner := NewDockerRunner(envvarHelper)
	pipelineRunner := NewPipelineRunner(envvarHelper, whenEvaluator, dockerRunner)
	endOfLifeHelper := NewEndOfLifeHelper(envvarHelper)

	// detect controlling server
	ciServer := envvarHelper.getEstafetteEnv("ESTAFETTE_CI_SERVER")

	if ciServer == "gocd" {

		// pretty print for go.cd integration
		log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().
			Timestamp().
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msg("Starting estafette-ci-builder...")

		// read yaml
		manifest, err := manifest.ReadManifestFromFile(".estafette.yaml")
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Reading .estafette.yaml manifest failed")
		}

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Getting current working directory failed")
		}

		log.Info().Msgf("Running %v pipelines", len(manifest.Pipelines))

		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Setting global environment variables failed")
		}

		// collect estafette and 'global' envvars from manifest
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvars(manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(manifest)

		// merge estafette and global envvars
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)

		result, err := pipelineRunner.runPipelines(manifest, dir, envvars)
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Executing pipelines from manifest failed")
		}

		renderStats(result)

		handleExit(result)

	} else if ciServer == "estafette" {

		// log as severity for stackdriver logging to recognize the level
		zerolog.LevelFieldName = "severity"

		gitName := envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_NAME")
		gitBranch := envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_BRANCH")
		gitRevision := envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_REVISION")
		jobName := envvarHelper.getEstafetteEnv("ESTAFETTE_BUILD_JOB_NAME")
		builderTrack := envvarHelper.getEstafetteEnv("ESTAFETTE_CI_BUILDER_TRACK")
		if builderTrack == "" {
			builderTrack = "stable"
		}

		buildLog := contracts.BuildLog{
			RepoSource:   envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_SOURCE"),
			RepoOwner:    strings.Split(gitName, "/")[0],
			RepoName:     strings.Split(gitName, "/")[1],
			RepoBranch:   gitBranch,
			RepoRevision: gitRevision,
			Steps:        make([]contracts.BuildLogStep, 0),
		}

		// log to file and stdout
		logFile, err := os.OpenFile("/log.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to create log file log.txt")
		}
		defer logFile.Close()
		multiLogWriter := io.MultiWriter(os.Stdout, logFile)

		// set some default fields added to all logs
		log.Logger = zerolog.New(multiLogWriter).With().
			Timestamp().
			Str("app", "estafette-ci-builder").
			Str("version", version).
			Str("jobName", jobName).
			Str("gitName", gitName).
			Str("gitBranch", gitBranch).
			Str("gitRevision", gitRevision).
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msg("Starting estafette-ci-builder...")

		// start docker daemon
		err = dockerRunner.startDockerDaemon()
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Error starting docker daemon")
		}

		// wait for docker daemon to be ready for usage
		dockerRunner.waitForDockerDaemon()

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Getting current working directory failed")
		}

		// set some envvars
		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Setting global environment variables failed")
		}

		// run git clone via pipeline runner
		estafetteGitCloneManifest := manifest.EstafetteManifest{
			Pipelines: []*manifest.EstafettePipeline{
				&manifest.EstafettePipeline{
					Name:             "git-clone",
					ContainerImage:   fmt.Sprintf("extensions/git-clone:%v", builderTrack),
					Shell:            "/bin/sh",
					WorkingDirectory: "/estafette-work",
					When:             "status == 'succeeded'",
					AutoInjected:     true,
				},
			},
		}

		log.Info().Msgf("Starting build version %v...", envvarHelper.getEstafetteEnv("ESTAFETTE_BUILD_VERSION"))

		// collect estafette envvars and run the git clone step
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvars(estafetteGitCloneManifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(estafetteGitCloneManifest)
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)
		gitCloneResult, err := pipelineRunner.runPipelines(estafetteGitCloneManifest, dir, envvars)
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Executing git clone step failed")
		}

		// check if manifest exists
		if !manifest.Exists(".estafette.yaml") {
			log.Info().Msg(".estafette.yaml file does not exist, exiting...")
			endOfLifeHelper.sendBuildFinishedEvent("nomanifest")
			os.Exit(0)
		}

		// read .estafette.yaml manifest
		manifest, err := manifest.ReadManifestFromFile(".estafette.yaml")
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Reading .estafette.yaml manifest failed")
		}

		// collect estafette envvars and run pipelines from manifest
		log.Info().Msgf("Running %v pipelines", len(manifest.Pipelines))
		estafetteEnvvars = envvarHelper.collectEstafetteEnvvars(manifest)
		globalEnvvars = envvarHelper.collectGlobalEnvvars(manifest)
		envvars = envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)
		result, err := pipelineRunner.runPipelines(manifest, dir, envvars)
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Executing pipelines from manifest failed")
		}

		// merge git clone and manifest result
		result.PipelineResults = append(gitCloneResult.PipelineResults, result.PipelineResults...)

		// send result to ci-api
		log.Info().Interface("result", result).Msg("Finished running pipelines")
		buildLog.Steps = transformPipelineRunResultToBuildLogSteps(result)
		endOfLifeHelper.sendBuildJobLogEvent(buildLog)
		buildStatus := "succeeded"
		if result.HasErrors() {
			buildStatus = "failed"
		}
		endOfLifeHelper.sendBuildFinishedEvent(buildStatus)
		os.Exit(0)
	}
}
<commit_msg>read manifest from envvar instead of from file after git-clone<commit_after>package main

import (
	"encoding/json"
	"io"
	"os"
	"runtime"
	"strings"

	"github.com/alecthomas/kingpin"
	"github.com/estafette/estafette-ci-contracts"
	crypt "github.com/estafette/estafette-ci-crypt"
	manifest "github.com/estafette/estafette-ci-manifest"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"

	stdlog "log"
)

var (
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()

	secretDecryptionKey = kingpin.Flag("secret-decryption-key", "The AES-256 key used to decrypt secrets that have been encrypted with it.").String()
)

func main() {

	// parse command line parameters
	kingpin.Parse()

	// bootstrap
	secretHelper := crypt.NewSecretHelper(*secretDecryptionKey)
	envvarHelper := NewEnvvarHelper("ESTAFETTE_", secretHelper)
	whenEvaluator := NewWhenEvaluator(envvarHelper)
	dockerRunner := NewDockerRunner(envvarHelper)
	pipelineRunner := NewPipelineRunner(envvarHelper, whenEvaluator, dockerRunner)
	endOfLifeHelper := NewEndOfLifeHelper(envvarHelper)

	// detect controlling server
	ciServer := envvarHelper.getEstafetteEnv("ESTAFETTE_CI_SERVER")

	if ciServer == "gocd" {

		// pretty print for go.cd integration
		log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr}).With().
			Timestamp().
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msg("Starting estafette-ci-builder...")

		// read yaml
		manifest, err := manifest.ReadManifestFromFile(".estafette.yaml")
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Reading .estafette.yaml manifest failed")
		}

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Getting current working directory failed")
		}

		log.Info().Msgf("Running %v pipelines", len(manifest.Pipelines))

		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Setting global environment variables failed")
		}

		// collect estafette and 'global' envvars from manifest
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvars(manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(manifest)

		// merge estafette and global envvars
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)

		result, err := pipelineRunner.runPipelines(manifest, dir, envvars)
		if err != nil {
			endOfLifeHelper.handleGocdFatal(err, "Executing pipelines from manifest failed")
		}

		renderStats(result)

		handleExit(result)

	} else if ciServer == "estafette" {

		// log as severity for stackdriver logging to recognize the level
		zerolog.LevelFieldName = "severity"

		gitName := envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_NAME")
		gitBranch := envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_BRANCH")
		gitRevision := envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_REVISION")
		jobName := envvarHelper.getEstafetteEnv("ESTAFETTE_BUILD_JOB_NAME")
		builderTrack := envvarHelper.getEstafetteEnv("ESTAFETTE_CI_BUILDER_TRACK")
		if builderTrack == "" {
			builderTrack = "stable"
		}

		buildLog := contracts.BuildLog{
			RepoSource:   envvarHelper.getEstafetteEnv("ESTAFETTE_GIT_SOURCE"),
			RepoOwner:    strings.Split(gitName, "/")[0],
			RepoName:     strings.Split(gitName, "/")[1],
			RepoBranch:   gitBranch,
			RepoRevision: gitRevision,
			Steps:        make([]contracts.BuildLogStep, 0),
		}

		// log to file and stdout
		logFile, err := os.OpenFile("/log.txt", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666)
		if err != nil {
			log.Fatal().Err(err).Msg("Failed to create log file log.txt")
		}
		defer logFile.Close()
		multiLogWriter := io.MultiWriter(os.Stdout, logFile)

		// set some default fields added to all logs
		log.Logger = zerolog.New(multiLogWriter).With().
			Timestamp().
			Str("app", "estafette-ci-builder").
			Str("version", version).
			Str("jobName", jobName).
			Str("gitName", gitName).
			Str("gitBranch", gitBranch).
			Str("gitRevision", gitRevision).
			Logger()

		stdlog.SetFlags(0)
		stdlog.SetOutput(log.Logger)

		// log startup message
		log.Info().
			Str("branch", branch).
			Str("revision", revision).
			Str("buildDate", buildDate).
			Str("goVersion", goVersion).
			Msg("Starting estafette-ci-builder...")

		// start docker daemon
		err = dockerRunner.startDockerDaemon()
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Error starting docker daemon")
		}

		// wait for docker daemon to be ready for usage
		dockerRunner.waitForDockerDaemon()

		// get current working directory
		dir, err := os.Getwd()
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Getting current working directory failed")
		}

		// set some envvars
		err = envvarHelper.setEstafetteGlobalEnvvars()
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Setting global environment variables failed")
		}

		// get manifest from envvar and unmarshal
		manifestJSON := os.Getenv("ESTAFETTE_CI_MANIFEST_JSON")
		var manifest manifest.EstafetteManifest
		json.Unmarshal([]byte(manifestJSON), &manifest)

		log.Info().Msgf("Starting build version %v...", envvarHelper.getEstafetteEnv("ESTAFETTE_BUILD_VERSION"))

		// collect estafette envvars and run pipelines from manifest
		log.Info().Msgf("Running %v pipelines", len(manifest.Pipelines))
		estafetteEnvvars := envvarHelper.collectEstafetteEnvvars(manifest)
		globalEnvvars := envvarHelper.collectGlobalEnvvars(manifest)
		envvars := envvarHelper.overrideEnvvars(estafetteEnvvars, globalEnvvars)
		result, err := pipelineRunner.runPipelines(manifest, dir, envvars)
		if err != nil {
			endOfLifeHelper.handleFatal(buildLog, err, "Executing pipelines from manifest failed")
		}

		// send result to ci-api
		log.Info().Interface("result", result).Msg("Finished running pipelines")
		buildLog.Steps = transformPipelineRunResultToBuildLogSteps(result)
		endOfLifeHelper.sendBuildJobLogEvent(buildLog)
		buildStatus := "succeeded"
		if result.HasErrors() {
			buildStatus = "failed"
		}
		endOfLifeHelper.sendBuildFinishedEvent(buildStatus)
		os.Exit(0)
	}
}
<|endoftext|>