// Exit with either an error or a panic func (s *SoftExit) Exit(v ...interface{}) error { if s.options.Debug { // Clearly this will cause it's own exit if it gets called. util.RootLogger().Panicln(v...) } util.RootLogger().Errorln(v...) return fmt.Errorf("Exiting.") }
func getServerVersion(channel string) (*util.Versions, error) { logger := util.RootLogger().WithField("Logger", "getServerVersion") url := fmt.Sprintf("https://s3.amazonaws.com/downloads.wercker.com/cli/%s/version.json", channel) nv := &util.Versions{} client := &http.Client{} req, err := http.NewRequest("GET", url, nil) if err != nil { logger.WithField("Error", err).Debug("Unable to create request to version endpoint") return nil, err } res, err := client.Do(req) if err != nil { logger.WithField("Error", err).Debug("Unable to execute HTTP request to version endpoint") return nil, err } body, err := ioutil.ReadAll(res.Body) if err != nil { logger.WithField("Error", err).Debug("Unable to read response body") return nil, err } err = json.Unmarshal(body, nv) if err != nil { logger.WithField("Error", err).Debug("Unable to unmarshal versions") return nil, err } return nv, nil }
// TODO(mies): maybe move to util.go at some point func getYml(detected string, options *core.DetectOptions) { logger := util.RootLogger().WithField("Logger", "Main") yml := "wercker.yml" if _, err := os.Stat(yml); err == nil { logger.Println(yml, "already exists. Do you want to overwrite? (yes/no)") if !askForConfirmation() { logger.Println("Exiting...") os.Exit(1) } } url := fmt.Sprintf("%s/api/v2/yml/%s", options.BaseURL, detected) res, err := http.Get(url) if err != nil { logger.WithField("Error", err).Error("Unable to reach wercker API") os.Exit(1) } defer res.Body.Close() body, err := ioutil.ReadAll(res.Body) if err != nil { logger.WithField("Error", err).Error("Unable to read response") } err = ioutil.WriteFile("wercker.yml", body, 0644) if err != nil { logger.WithField("Error", err).Error("Unable to write wercker.yml file") } }
func NewStep(config *core.StepConfig, options *core.PipelineOptions, dockerOptions *DockerOptions) (core.Step, error) { // NOTE(termie) Special case steps are special if config.ID == "internal/docker-push" { return NewDockerPushStep(config, options, dockerOptions) } if config.ID == "internal/docker-scratch-push" { return NewDockerScratchPushStep(config, options, dockerOptions) } if config.ID == "internal/store-container" { return NewStoreContainerStep(config, options, dockerOptions) } if strings.HasPrefix(config.ID, "internal/") { if !options.EnableDevSteps { util.RootLogger().Warnln("Ignoring dev step:", config.ID) return nil, nil } } if options.EnableDevSteps { if config.ID == "internal/watch" { return NewWatchStep(config, options, dockerOptions) } if config.ID == "internal/shell" { return NewShellStep(config, options, dockerOptions) } } return NewDockerStep(config, options, dockerOptions) }
// DumpOptions prints out a sorted list of options func DumpOptions(options interface{}, indent ...string) { indent = append(indent, " ") s := reflect.ValueOf(options).Elem() typeOfT := s.Type() names := []string{} for i := 0; i < s.NumField(); i++ { // f := s.Field(i) fieldName := typeOfT.Field(i).Name if fieldName != "HostEnv" { names = append(names, fieldName) } } sort.Strings(names) logger := util.RootLogger().WithField("Logger", "Options") for _, name := range names { r := reflect.ValueOf(options) f := reflect.Indirect(r).FieldByName(name) if strings.HasSuffix(name, "Options") { if len(indent) > 1 && name == "GlobalOptions" { continue } logger.Debugln(fmt.Sprintf("%s%s %s", strings.Join(indent, ""), name, f.Type())) DumpOptions(f.Interface(), indent...) } else { logger.Debugln(fmt.Sprintf("%s%s %s = %v", strings.Join(indent, ""), name, f.Type(), f.Interface())) } } }
// NewDeployOptions constructor func NewDeployOptions(c util.Settings, e *util.Environment) (*PipelineOptions, error) { pipelineOpts, err := NewPipelineOptions(c, e) if err != nil { return nil, err } // default to last build output if none defined target, _ := c.String("target") if target == "" { found, err := util.Exists("./.wercker/latest/output") if err == nil && found { util.RootLogger().Println("No target specified, using recent build output.") pipelineOpts.ProjectPath, _ = filepath.Abs("./.wercker/latest/output") } } // if the deploy target path does not have a wercker.yml, use the current one werckerYml, _ := c.String("wercker-yml") if werckerYml == "" { found, _ := util.Exists(filepath.Join(pipelineOpts.ProjectPath, "wercker.yml")) if !found { pipelineOpts.WerckerYml = "./wercker.yml" } } if pipelineOpts.RunID == "" { pipelineOpts.RunID = uuid.NewRandom().String() } return pipelineOpts, nil }
func normalizeRegistry(address string) string { logger := util.RootLogger().WithField("Logger", "Docker") if address == "" { logger.Debugln("No registry address provided, using https://registry.hub.docker.com") return "https://registry.hub.docker.com/v1/" } parsed, err := url.Parse(address) if err != nil { logger.Errorln("Registry address is invalid, this will probably fail:", address) return address } if parsed.Scheme != "https" { logger.Warnln("Registry address is expected to begin with 'https://', forcing it to use https") parsed.Scheme = "https" address = parsed.String() } if strings.HasSuffix(address, "/") { address = address[:len(address)-1] } parts := strings.Split(address, "/") possiblyAPIVersionStr := parts[len(parts)-1] // we only support v1, so... if possiblyAPIVersionStr == "v2" { logger.Warnln("Registry API v2 not supported, using v1") newParts := append(parts[:len(parts)-1], "v1") address = strings.Join(newParts, "/") } else if possiblyAPIVersionStr != "v1" { newParts := append(parts, "v1") address = strings.Join(newParts, "/") } return address + "/" }
// NewDockerPushStep is a special step for doing docker pushes func NewDockerPushStep(stepConfig *core.StepConfig, options *core.PipelineOptions, dockerOptions *DockerOptions) (*DockerPushStep, error) { name := "docker-push" displayName := "docker push" if stepConfig.Name != "" { displayName = stepConfig.Name } // Add a random number to the name to prevent collisions on disk stepSafeID := fmt.Sprintf("%s-%s", name, uuid.NewRandom().String()) baseStep := core.NewBaseStep(core.BaseStepOptions{ DisplayName: displayName, Env: &util.Environment{}, ID: name, Name: name, Owner: "wercker", SafeID: stepSafeID, Version: util.Version(), }) return &DockerPushStep{ BaseStep: baseStep, data: stepConfig.Data, logger: util.RootLogger().WithField("Logger", "DockerPushStep"), options: options, dockerOptions: dockerOptions, }, nil }
// NewDockerClient based on options and env func NewDockerClient(options *DockerOptions) (*DockerClient, error) { dockerHost := options.DockerHost tlsVerify := options.DockerTLSVerify logger := util.RootLogger().WithField("Logger", "Docker") var ( client *docker.Client err error ) if tlsVerify == "1" { // We're using TLS, let's locate our certs and such // boot2docker puts its certs at... dockerCertPath := options.DockerCertPath // TODO(termie): maybe fast-fail if these don't exist? cert := path.Join(dockerCertPath, fmt.Sprintf("cert.pem")) ca := path.Join(dockerCertPath, fmt.Sprintf("ca.pem")) key := path.Join(dockerCertPath, fmt.Sprintf("key.pem")) client, err = docker.NewVersionnedTLSClient(dockerHost, cert, key, ca, "") if err != nil { return nil, err } } else { client, err = docker.NewClient(dockerHost) if err != nil { return nil, err } } return &DockerClient{Client: client, logger: logger}, nil }
// NewDockerFileCollector constructor func NewDockerFileCollector(client *DockerClient, containerID string) *DockerFileCollector { return &DockerFileCollector{ client: client, containerID: containerID, logger: util.RootLogger().WithField("Logger", "DockerFileCollector"), } }
// NewDockerBox from a name and other references func NewDockerBox(boxConfig *core.BoxConfig, options *core.PipelineOptions, dockerOptions *DockerOptions) (*DockerBox, error) { name := boxConfig.ID if strings.Contains(name, "@") { return nil, fmt.Errorf("Invalid box name, '@' is not allowed in docker repositories.") } parts := strings.Split(name, ":") repository := parts[0] tag := "latest" if len(parts) > 1 { tag = parts[1] } if boxConfig.Tag != "" { tag = boxConfig.Tag } name = fmt.Sprintf("%s:%s", repository, tag) repoParts := strings.Split(repository, "/") shortName := repository if len(repoParts) > 1 { shortName = repoParts[len(repoParts)-1] } networkDisabled := false cmd := boxConfig.Cmd if cmd == "" { cmd = "/bin/bash" } entrypoint := boxConfig.Entrypoint logger := util.RootLogger().WithFields(util.LogFields{ "Logger": "Box", "Name": name, "ShortName": shortName, }) client, err := NewDockerClient(dockerOptions) if err != nil { return nil, err } return &DockerBox{ Name: name, ShortName: shortName, client: client, config: boxConfig, options: options, dockerOptions: dockerOptions, repository: repository, tag: tag, networkDisabled: networkDisabled, logger: logger, cmd: cmd, entrypoint: entrypoint, volumes: []string{}, }, nil }
// NewDockerTransport constructor func NewDockerTransport(options *core.PipelineOptions, dockerOptions *DockerOptions, containerID string) (core.Transport, error) { client, err := NewDockerClient(dockerOptions) if err != nil { return nil, err } logger := util.RootLogger().WithField("Logger", "DockerTransport") return &DockerTransport{options: options, client: client, containerID: containerID, logger: logger}, nil }
// NewExternalServiceBox gives us an ExternalServiceBox from config func NewExternalServiceBox(boxConfig *core.BoxConfig, options *core.PipelineOptions, dockerOptions *DockerOptions, builder Builder) (*ExternalServiceBox, error) { logger := util.RootLogger().WithField("Logger", "ExternalService") box := &DockerBox{options: options, dockerOptions: dockerOptions} return &ExternalServiceBox{ InternalServiceBox: &InternalServiceBox{DockerBox: box, logger: logger}, externalConfig: boxConfig, builder: builder, }, nil }
// NewSession returns a new interactive session to a container. func NewSession(options *PipelineOptions, transport Transport) *Session { logger := util.RootLogger().WithField("Logger", "Session") return &Session{ options: options, transport: transport, logsHidden: false, logger: logger, } }
// AskForUpdate asks users if they want to update and returns the answer func AskForUpdate() bool { fmt.Println("Would you like update? [yN]") reader := bufio.NewReader(os.Stdin) line, err := reader.ReadString('\n') if err != nil { util.RootLogger().Errorln("Problem reading answer", err) return false } return strings.HasPrefix(strings.ToLower(line), "y") }
// NewArtificer returns an Artificer func NewArtificer(options *core.PipelineOptions, dockerOptions *DockerOptions) *Artificer { logger := util.RootLogger().WithField("Logger", "Artificer") s3store := core.NewS3Store(options.AWSOptions) return &Artificer{ options: options, logger: logger, store: s3store, } }
// NewAPIClient returns our dumb client func NewAPIClient(options *APIOptions) *APIClient { logger := util.RootLogger().WithFields(util.LogFields{ "Logger": "API", }) return &APIClient{ baseURL: options.BaseURL, client: &http.Client{}, options: options, logger: logger, } }
func cmdLogout(options *core.LogoutOptions) error { soft := NewSoftExit(options.GlobalOptions) logger := util.RootLogger().WithField("Logger", "Main") logger.Println("Logging out") err := removeToken(options.GlobalOptions.AuthTokenStore) if err != nil { return soft.Exit(err) } return nil }
// NewUpdater constructor func NewUpdater(channel string) (*Updater, error) { serverVersion, err := getServerVersion(channel) if err != nil { return nil, err } return &Updater{ CurrentVersion: util.GetVersions(), ServerVersion: serverVersion, channel: channel, l: util.RootLogger().WithField("Logger", "Updater"), }, nil }
// NewRunner from global options func NewRunner(ctx context.Context, options *core.PipelineOptions, dockerOptions *dockerlocal.DockerOptions, getPipeline pipelineGetter) (*Runner, error) { e, err := core.EmitterFromContext(ctx) if err != nil { return nil, err } logger := util.RootLogger().WithField("Logger", "Runner") // h, err := NewLogHandler() // if err != nil { // p.logger.WithField("Error", err).Panic("Unable to LogHandler") // } // h.ListenTo(e) if options.Debug { dh := core.NewDebugHandler() dh.ListenTo(e) } l, err := event.NewLiteralLogHandler(options) if err != nil { logger.WithField("Error", err).Panic("Unable to event.LiteralLogHandler") } l.ListenTo(e) var mh *event.MetricsEventHandler if options.ShouldKeenMetrics { mh, err = event.NewMetricsHandler(options) if err != nil { logger.WithField("Error", err).Panic("Unable to MetricsHandler") } mh.ListenTo(e) } var r *event.ReportHandler if options.ShouldReport { r, err := event.NewReportHandler(options.ReporterHost, options.ReporterKey) if err != nil { logger.WithField("Error", err).Panic("Unable to event.ReportHandler") } r.ListenTo(e) } return &Runner{ options: options, dockerOptions: dockerOptions, literalLogger: l, metrics: mh, reporter: r, getPipeline: getPipeline, logger: logger, emitter: e, formatter: &util.Formatter{options.GlobalOptions.ShowColors}, }, nil }
func getCollection(options *core.PipelineOptions) string { if options.BuildID != "" { return "build-events" } if options.DeployID != "" { return "deploy-events" } util.RootLogger().WithField("Logger", "Metrics").Panic("Metrics is only able to send metrics for builds or deploys") return "" }
func (t *FakeTransport) Attach(sessionCtx context.Context, stdin io.Reader, stdout, stderr io.Writer) (context.Context, error) { fakeContext, cancel := context.WithCancel(sessionCtx) t.cancelFunc = cancel t.stdin = stdin t.stdout = stdout t.stderr = stderr t.inchan = make(chan string) t.outchan = make(chan string) go func() { for { var p []byte p = make([]byte, 1024) i, err := t.stdin.Read(p) s := string(p[:i]) util.RootLogger().Println(fmt.Sprintf("(test) stdin: %q", s)) t.inchan <- s if err != nil { close(t.inchan) return } } }() go func() { for { s := <-t.outchan util.RootLogger().Println(fmt.Sprintf("(test) stdout: %q", s)) _, err := t.stdout.Write([]byte(s)) if err != nil { close(t.outchan) return } } }() return fakeContext, nil }
// NewLiteralLogHandler will create a new LiteralLogHandler. func NewLiteralLogHandler(options *core.PipelineOptions) (*LiteralLogHandler, error) { var logger *util.Logger if options.Debug { logger = util.RootLogger() } else { logger = util.NewLogger() logger.Formatter = &reporter.LiteralFormatter{} logger.Level = log.InfoLevel } return &LiteralLogHandler{l: logger, options: options}, nil }
// detectProject inspects the the current directory that wercker is running in // and detects the project's programming language func cmdDetect(options *core.DetectOptions) error { soft := NewSoftExit(options.GlobalOptions) logger := util.RootLogger().WithField("Logger", "Main") logger.Println("########### Detecting your project! #############") detected := "" d, err := os.Open(".") if err != nil { logger.WithField("Error", err).Error("Unable to open directory") soft.Exit(err) } defer d.Close() files, err := d.Readdir(-1) if err != nil { logger.WithField("Error", err).Error("Unable to read directory") soft.Exit(err) } outer: for _, f := range files { switch { case f.Name() == "package.json": detected = "nodejs" break outer case f.Name() == "requirements.txt": detected = "python" break outer case f.Name() == "Gemfile": detected = "ruby" break outer case filepath.Ext(f.Name()) == ".go": detected = "golang" break outer } } if detected == "" { logger.Println("No stack detected, generating default wercker.yml") detected = "default" } else { logger.Println("Detected:", detected) logger.Println("Generating wercker.yml") } getYml(detected, options) return nil }
// NewS3Store creates a new S3Store func NewS3Store(options *AWSOptions) *S3Store { logger := util.RootLogger().WithField("Logger", "S3Store") if options == nil { logger.Panic("options cannot be nil") } client := s3.New(&aws.Config{Region: &options.AWSRegion}) return &S3Store{ client: client, logger: logger, options: options, } }
// NewReportHandler will create a new ReportHandler. func NewReportHandler(werckerHost, token string) (*ReportHandler, error) { r, err := reporter.New(werckerHost, token) if err != nil { return nil, err } writers := make(map[string]*reporter.LogWriter) logger := util.RootLogger().WithField("Logger", "Reporter") h := &ReportHandler{ reporter: r, writers: writers, logger: logger, } return h, nil }
// NewArtificer returns an Artificer func NewArtificer(options *core.PipelineOptions, dockerOptions *DockerOptions) *Artificer { logger := util.RootLogger().WithField("Logger", "Artificer") var store core.Store if options.ShouldStoreS3 { store = core.NewS3Store(options.AWSOptions) } return &Artificer{ options: options, dockerOptions: dockerOptions, logger: logger, store: store, } }
// Retrieving user input utility functions func askForConfirmation() bool { var response string _, err := fmt.Scanln(&response) if err != nil { util.RootLogger().WithField("Logger", "Util").Fatal(err) } response = strings.ToLower(response) if strings.HasPrefix(response, "y") { return true } else if strings.HasPrefix(response, "n") { return false } else { println("Please type yes or no and then press enter:") return askForConfirmation() } }
// guessAuthToken will attempt to read from the token store location if // no auth token was provided func guessAuthToken(c util.Settings, e *util.Environment, authTokenStore string) string { token, _ := c.GlobalString("auth-token") if token != "" { return token } if foundToken, _ := util.Exists(authTokenStore); !foundToken { return "" } tokenBytes, err := ioutil.ReadFile(authTokenStore) if err != nil { util.RootLogger().WithField("Logger", "Options").Errorln(err) return "" } return strings.TrimSpace(string(tokenBytes)) }
// CheckAccess checks whether a user can read or write an image // TODO(termie): this really uses the docker registry code rather than the // client so, maybe this is the wrong place func (c *DockerClient) CheckAccess(opts CheckAccessOptions) (bool, error) { logger := util.RootLogger().WithField("Logger", "Docker") logger.Debug("Checking access for ", opts.Repository) // Do the steps described here: https://gist.github.com/termie/bc0334b086697a162f67 name := normalizeRepo(opts.Repository) logger.Debug("Normalized repo ", name) auth := registry.BasicAuth{ Username: opts.Auth.Username, Password: opts.Auth.Password, } client := registry.NewClient() reg := normalizeRegistry(opts.Registry) logger.Debug("Normalized Registry ", reg) client.BaseURL, _ = url.Parse(reg) if opts.Access == "write" { if _, err := client.Hub.GetWriteToken(name, auth); err != nil { if err.Error() == "Server returned status 401" || err.Error() == "Server returned status 403" { return false, nil } return false, err } } else if opts.Access == "read" { if opts.Auth.Username != "" { if _, err := client.Hub.GetReadTokenWithAuth(name, auth); err != nil { if err.Error() == "Server returned status 401" || err.Error() == "Server returned status 403" { return false, nil } return false, err } } else { if _, err := client.Hub.GetReadToken(name); err != nil { if err.Error() == "Server returned status 401" || err.Error() == "Server returned status 403" { return false, nil } return false, err } } } else { return false, fmt.Errorf("Invalid access type requested: %s", opts.Access) } return true, nil }