Ejemplo n.º 1
0
func newCmdRebuild(cfg *api.Config) *cobra.Command {
	buildCmd := &cobra.Command{
		Use:   "rebuild <image> [<new-tag>]",
		Short: "Rebuild an existing image",
		Long:  "Rebuild an existing application image that was built by S2I previously.",
		Run: func(cmd *cobra.Command, args []string) {
			// If user specifies the arguments, then we override the stored ones
			if len(args) >= 1 {
				cfg.Tag = args[0]
			} else {
				cmd.Help()
				os.Exit(1)
			}

			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				defer r.Close()
				cfg.PullAuthentication = docker.LoadAndGetImageRegistryAuth(r, cfg.Tag)
			}

			err := build.GenerateConfigFromLabels(cfg)
			checkErr(err)

			if len(args) >= 2 {
				cfg.Tag = args[1]
			}

			// Attempt to read the .dockercfg and extract the authentication for
			// docker pull
			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				defer r.Close()
				cfg.PullAuthentication = docker.LoadAndGetImageRegistryAuth(r, cfg.BuilderImage)
			}

			if len(cfg.BuilderPullPolicy) == 0 {
				cfg.BuilderPullPolicy = api.DefaultBuilderPullPolicy
			}
			if len(cfg.PreviousImagePullPolicy) == 0 {
				cfg.PreviousImagePullPolicy = api.DefaultPreviousImagePullPolicy
			}

			if glog.V(2) {
				fmt.Printf("\n%s\n", describe.DescribeConfig(cfg))
			}

			builder, err := strategies.GetStrategy(cfg)
			checkErr(err)
			result, err := builder.Build(cfg)
			checkErr(err)

			for _, message := range result.Messages {
				glog.V(1).Infof(message)
			}

		},
	}

	cmdutil.AddCommonFlags(buildCmd, cfg)
	return buildCmd
}
Ejemplo n.º 2
0
func newCmdRebuild(cfg *api.Config) *cobra.Command {
	buildCmd := &cobra.Command{
		Use:   "rebuild <image> [<new-tag>]",
		Short: "Rebuild an existing image",
		Long:  "Rebuild an existing application image that was build by S2I previously.",
		Run: func(cmd *cobra.Command, args []string) {
			// If user specifies the arguments, then we override the stored ones
			if len(args) >= 0 {
				cfg.Tag = args[0]
			} else {
				cmd.Help()
				os.Exit(1)
			}

			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				cfg.PullAuthentication = docker.GetImageRegistryAuth(r, cfg.Tag)
			}

			err := build.GenerateConfigFromLabels(cfg.Tag, cfg)
			checkErr(err)

			if len(args) >= 2 {
				cfg.Tag = args[1]
			}

			// Attempt to read the .dockercfg and extract the authentication for
			// docker pull
			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				cfg.PullAuthentication = docker.GetImageRegistryAuth(r, cfg.BuilderImage)
			}

			if glog.V(2) {
				fmt.Printf("\n%s\n", describe.DescribeConfig(cfg))
			}

			builder, err := strategies.GetStrategy(cfg)
			checkErr(err)
			result, err := builder.Build(cfg)
			checkErr(err)

			for _, message := range result.Messages {
				glog.V(1).Infof(message)
			}

		},
	}

	buildCmd.Flags().BoolVarP(&(cfg.Quiet), "quiet", "q", false, "Operate quietly. Suppress all non-error output.")
	buildCmd.Flags().BoolVar(&(cfg.Incremental), "incremental", true, "Perform an incremental build")
	buildCmd.Flags().BoolVar(&(cfg.RemovePreviousImage), "rm", false, "Remove the previous image during incremental builds")
	buildCmd.Flags().StringVar(&(cfg.CallbackURL), "callback-url", "", "Specify a URL to invoke via HTTP POST upon build completion")
	buildCmd.Flags().BoolVar(&(cfg.ForcePull), "force-pull", true, "Always pull the builder image even if it is present locally")
	buildCmd.Flags().BoolVar(&(cfg.PreserveWorkingDir), "save-temp-dir", false, "Save the temporary directory used by STI instead of deleting it")
	buildCmd.Flags().StringVarP(&(cfg.DockerCfgPath), "dockercfg-path", "", filepath.Join(os.Getenv("HOME"), ".dockercfg"), "Specify the path to the Docker configuration file")
	return buildCmd
}
Ejemplo n.º 3
0
// executes STI build based on configured builder, S2I builder factory and S2I config validator
func (s *STIBuilder) Build() error {

	var push bool

	// if there is no output target, set one up so the docker build logic
	// (which requires a tag) will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Spec.Output.To = &kapi.ObjectReference{
			Kind: "DockerImage",
			Name: noOutputDefaultTag,
		}
		push = false
	} else {
		push = true
	}
	tag := s.build.Spec.Output.To.Name

	config := &stiapi.Config{
		BuilderImage:   s.build.Spec.Strategy.SourceStrategy.From.Name,
		DockerConfig:   &stiapi.DockerConfig{Endpoint: s.dockerSocket},
		Source:         s.build.Spec.Source.Git.URI,
		ContextDir:     s.build.Spec.Source.ContextDir,
		DockerCfgPath:  os.Getenv(dockercfg.PullAuthType),
		Tag:            tag,
		ScriptsURL:     s.build.Spec.Strategy.SourceStrategy.Scripts,
		Environment:    buildEnvVars(s.build),
		LabelNamespace: api.DefaultDockerLabelNamespace,
		Incremental:    s.build.Spec.Strategy.SourceStrategy.Incremental,
		ForcePull:      s.build.Spec.Strategy.SourceStrategy.ForcePull,
	}
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		s.build.Spec.Revision.Git.Commit != "" {
		config.Ref = s.build.Spec.Revision.Git.Commit
	} else if s.build.Spec.Source.Git.Ref != "" {
		config.Ref = s.build.Spec.Source.Git.Ref
	}

	allowedUIDs := os.Getenv("ALLOWED_UIDS")
	glog.V(2).Infof("The value of ALLOWED_UIDS is [%s]", allowedUIDs)
	if len(allowedUIDs) > 0 {
		err := config.AllowedUIDs.Set(allowedUIDs)
		if err != nil {
			return err
		}
	}

	if errs := s.configValidator.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	config.PullAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType)
	config.IncrementalAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(tag, dockercfg.PushAuthType)

	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := s.builderFactory.GetStrategy(config)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	// Set the HTTP and HTTPS proxies to be used by the S2I build.
	originalProxies := setHTTPProxy(s.build.Spec.Source.Git.HTTPProxy, s.build.Spec.Source.Git.HTTPSProxy)

	if _, err = builder.Build(config); err != nil {
		return err
	}

	// Reset proxies back to their original value.
	resetHTTPProxy(originalProxies)

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			tag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", tag)
		} else {
			glog.Infof("No push secret provided")
		}
		glog.Infof("Pushing %s image ...", tag)
		if err := pushImage(s.dockerClient, tag, pushAuthConfig); err != nil {
			// write extended error message to assist in problem resolution
			msg := fmt.Sprintf("Failed to push image. Response from registry is: %v", err)
			if authPresent {
				glog.Infof("Registry server Address: %s", pushAuthConfig.ServerAddress)
				glog.Infof("Registry server User Name: %s", pushAuthConfig.Username)
				glog.Infof("Registry server Email: %s", pushAuthConfig.Email)
				passwordPresent := "<<empty>>"
				if len(pushAuthConfig.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.Infof("Registry server address: %s", passwordPresent)
			}
			return errors.New(msg)
		}
		glog.Infof("Successfully pushed %s", tag)
		glog.Flush()
	}
	return nil
}
Ejemplo n.º 4
0
// Build executes STI build based on configured builder, S2I builder factory and S2I config validator
func (s *STIBuilder) Build() error {
	var push bool

	contextDir := filepath.Clean(s.build.Spec.Source.ContextDir)
	if contextDir == "." || contextDir == "/" {
		contextDir = ""
	}
	buildDir, err := ioutil.TempDir("", "s2i-build")
	if err != nil {
		return err
	}
	srcDir := filepath.Join(buildDir, s2iapi.Source)
	if err := os.MkdirAll(srcDir, os.ModePerm); err != nil {
		return err
	}
	tmpDir := filepath.Join(buildDir, "tmp")
	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
		return err
	}

	download := &downloader{
		s:       s,
		in:      os.Stdin,
		timeout: urlCheckTimeout,

		dir:        srcDir,
		contextDir: contextDir,
		tmpDir:     tmpDir,
	}

	// if there is no output target, set one up so the docker build logic
	// (which requires a tag) will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Spec.Output.To = &kapi.ObjectReference{
			Kind: "DockerImage",
			Name: noOutputDefaultTag,
		}
		push = false
	} else {
		push = true
	}

	tag := s.build.Spec.Output.To.Name
	git := s.build.Spec.Source.Git

	var ref string
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		len(s.build.Spec.Revision.Git.Commit) != 0 {
		ref = s.build.Spec.Revision.Git.Commit
	} else if git != nil && len(git.Ref) != 0 {
		ref = git.Ref
	}

	sourceURI := &url.URL{
		Scheme:   "file",
		Path:     srcDir,
		Fragment: ref,
	}

	config := &s2iapi.Config{
		WorkingDir:     buildDir,
		DockerConfig:   &s2iapi.DockerConfig{Endpoint: s.dockerSocket},
		DockerCfgPath:  os.Getenv(dockercfg.PullAuthType),
		LabelNamespace: api.DefaultDockerLabelNamespace,

		ScriptsURL: s.build.Spec.Strategy.SourceStrategy.Scripts,

		BuilderImage: s.build.Spec.Strategy.SourceStrategy.From.Name,
		Incremental:  s.build.Spec.Strategy.SourceStrategy.Incremental,
		ForcePull:    s.build.Spec.Strategy.SourceStrategy.ForcePull,

		Environment:       buildEnvVars(s.build),
		DockerNetworkMode: getDockerNetworkMode(),

		Source:     sourceURI.String(),
		Tag:        tag,
		ContextDir: s.build.Spec.Source.ContextDir,
	}

	allowedUIDs := os.Getenv("ALLOWED_UIDS")
	glog.V(2).Infof("The value of ALLOWED_UIDS is [%s]", allowedUIDs)
	if len(allowedUIDs) > 0 {
		err := config.AllowedUIDs.Set(allowedUIDs)
		if err != nil {
			return err
		}
	}

	if errs := s.validator.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	config.PullAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType)
	config.IncrementalAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(tag, dockercfg.PushAuthType)

	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := s.builder.Builder(config, s2ibuild.Overrides{Downloader: download})
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	// Set the HTTP and HTTPS proxies to be used by the S2I build.
	var originalProxies map[string]string
	if git != nil {
		originalProxies = setHTTPProxy(git.HTTPProxy, git.HTTPSProxy)
	}

	if _, err = builder.Build(config); err != nil {
		return err
	}

	// Reset proxies back to their original value.
	resetHTTPProxy(originalProxies)

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			tag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", tag)
		} else {
			glog.Infof("No push secret provided")
		}
		glog.Infof("Pushing %s image ...", tag)
		if err := pushImage(s.dockerClient, tag, pushAuthConfig); err != nil {
			// write extended error message to assist in problem resolution
			msg := fmt.Sprintf("Failed to push image. Response from registry is: %v", err)
			if authPresent {
				glog.Infof("Registry server Address: %s", pushAuthConfig.ServerAddress)
				glog.Infof("Registry server User Name: %s", pushAuthConfig.Username)
				glog.Infof("Registry server Email: %s", pushAuthConfig.Email)
				passwordPresent := "<<empty>>"
				if len(pushAuthConfig.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.Infof("Registry server Password: %s", passwordPresent)
			}
			return errors.New(msg)
		}
		glog.Infof("Successfully pushed %s", tag)
		glog.Flush()
	}
	return nil
}
Ejemplo n.º 5
0
func newCmdBuild(cfg *api.Config) *cobra.Command {
	useConfig := false
	oldScriptsFlag := ""
	oldDestination := ""

	buildCmd := &cobra.Command{
		Use:   "build <source> <image> [<tag>]",
		Short: "Build a new image",
		Long:  "Build a new Docker image named <tag> (if provided) from a source repository and base image.",
		Example: `
# Build an application Docker image from a Git repository
$ s2i build git://github.com/openshift/ruby-hello-world centos/ruby-22-centos7 hello-world-app

# Build from a local directory
$ s2i build . centos/ruby-22-centos7 hello-world-app
`,
		Run: func(cmd *cobra.Command, args []string) {
			go cmdutil.InstallDumpOnSignal()

			// Attempt to restore the build command from the configuration file
			if useConfig {
				config.Restore(cfg, cmd)
			}

			// If user specifies the arguments, then we override the stored ones
			if len(args) >= 2 {
				cfg.Source = args[0]
				cfg.BuilderImage = args[1]
				if len(args) >= 3 {
					cfg.Tag = args[2]
				}
			}

			if cfg.ForcePull {
				glog.Warning("DEPRECATED: The '--force-pull' option is deprecated. Use '--pull-policy' instead")
			}

			if len(cfg.BuilderPullPolicy) == 0 {
				cfg.BuilderPullPolicy = api.DefaultBuilderPullPolicy
			}

			if errs := validation.ValidateConfig(cfg); len(errs) > 0 {
				for _, e := range errs {
					fmt.Fprintf(os.Stderr, "ERROR: %s\n", e)
				}
				fmt.Println()
				cmd.Help()
				os.Exit(1)
			}

			// Persists the current command line options and config into .stifile
			if useConfig {
				config.Save(cfg, cmd)
			}

			// Attempt to read the .dockercfg and extract the authentication for
			// docker pull
			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				auths := docker.LoadImageRegistryAuth(r)
				cfg.PullAuthentication = docker.GetImageRegistryAuth(auths, cfg.BuilderImage)
				if cfg.Incremental {
					cfg.IncrementalAuthentication = docker.GetImageRegistryAuth(auths, cfg.Tag)
				}
			}

			cfg.Environment = map[string]string{}
			if len(cfg.EnvironmentFile) > 0 {
				result, err := util.ReadEnvironmentFile(cfg.EnvironmentFile)
				if err != nil {
					glog.Warningf("Unable to read environment file %q: %v", cfg.EnvironmentFile, err)
				} else {
					cfg.Environment = result
				}
			}

			envs, err := cmdutil.ParseEnvs(cmd, "env")
			checkErr(err)
			for k, v := range envs {
				cfg.Environment[k] = v
			}

			if len(oldScriptsFlag) != 0 {
				glog.Warning("DEPRECATED: Flag --scripts is deprecated, use --scripts-url instead")
				cfg.ScriptsURL = oldScriptsFlag
			}
			if len(oldDestination) != 0 {
				glog.Warning("DEPRECATED: Flag --location is deprecated, use --destination instead")
				cfg.Destination = oldDestination
			}

			if glog.V(2) {
				fmt.Printf("\n%s\n", describe.DescribeConfig(cfg))
			}

			if !docker.IsReachable(cfg) {
				glog.Fatalf("Unable to connect to Docker daemon. Please set the DOCKER_HOST or make sure the Docker socket %q exists", cfg.DockerConfig.Endpoint)
			}

			builder, err := strategies.GetStrategy(cfg)
			checkErr(err)
			result, err := builder.Build(cfg)
			checkErr(err)

			for _, message := range result.Messages {
				glog.V(1).Infof(message)
			}

			if cfg.RunImage {
				runner, err := run.New(cfg)
				checkErr(err)
				err = runner.Run(cfg)
				checkErr(err)
			}

		},
	}

	cmdutil.AddCommonFlags(buildCmd, cfg)

	buildCmd.Flags().BoolVar(&(cfg.RunImage), "run", false, "Run resulting image as part of invocation of this command")
	buildCmd.Flags().BoolVar(&(cfg.DisableRecursive), "recursive", true, "Fetch all git submodules when cloning application repository")
	buildCmd.Flags().StringP("env", "e", "", "Specify an environment var NAME=VALUE,NAME2=VALUE2,...")
	buildCmd.Flags().StringVarP(&(cfg.Ref), "ref", "r", "", "Specify a ref to check-out")
	buildCmd.Flags().StringVarP(&(cfg.AssembleUser), "assemble-user", "", "", "Specify the user to run assemble with")
	buildCmd.Flags().StringVarP(&(cfg.ContextDir), "context-dir", "", "", "Specify the sub-directory inside the repository with the application sources")
	buildCmd.Flags().StringVarP(&(cfg.ScriptsURL), "scripts-url", "s", "", "Specify a URL for the assemble and run scripts")
	buildCmd.Flags().StringVar(&(oldScriptsFlag), "scripts", "", "DEPRECATED: Specify a URL for the assemble and run scripts")
	buildCmd.Flags().BoolVar(&(useConfig), "use-config", false, "Store command line options to .stifile")
	buildCmd.Flags().StringVarP(&(cfg.EnvironmentFile), "environment-file", "E", "", "Specify the path to the file with environment")
	buildCmd.Flags().StringVarP(&(cfg.DisplayName), "application-name", "n", "", "Specify the display name for the application (default: output image name)")
	buildCmd.Flags().StringVarP(&(cfg.Description), "description", "", "", "Specify the description of the application")
	buildCmd.Flags().VarP(&(cfg.AllowedUIDs), "allowed-uids", "u", "Specify a range of allowed user ids for the builder image")
	buildCmd.Flags().StringVarP(&(oldDestination), "location", "l", "",
		"DEPRECATED: Specify a destination location for untar operation")

	return buildCmd
}
Ejemplo n.º 6
0
func newCmdBuild(cfg *api.Config) *cobra.Command {
	useConfig := false
	oldScriptsFlag := ""
	oldDestination := ""

	buildCmd := &cobra.Command{
		Use:   "build <source> <image> [<tag>]",
		Short: "Build a new image",
		Long:  "Build a new Docker image named <tag> (if provided) from a source repository and base image.",
		Run: func(cmd *cobra.Command, args []string) {
			go func() {
				for {
					sigs := make(chan os.Signal, 1)
					signal.Notify(sigs, syscall.SIGQUIT)
					buf := make([]byte, 1<<20)
					for {
						<-sigs
						runtime.Stack(buf, true)
						if file, err := ioutil.TempFile(os.TempDir(), "sti_dump"); err == nil {
							defer file.Close()
							file.Write(buf)
						}
						glog.Infof("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf)
					}
				}
			}()
			// Attempt to restore the build command from the configuration file
			if useConfig {
				config.Restore(cfg, cmd)
			}

			// If user specifies the arguments, then we override the stored ones
			if len(args) >= 2 {
				cfg.Source = args[0]
				cfg.BuilderImage = args[1]
				if len(args) >= 3 {
					cfg.Tag = args[2]
				}
			}

			if len(validation.ValidateConfig(cfg)) != 0 {
				cmd.Help()
				os.Exit(1)
			}

			// Persists the current command line options and config into .stifile
			if useConfig {
				config.Save(cfg, cmd)
			}

			// Attempt to read the .dockercfg and extract the authentication for
			// docker pull
			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				cfg.PullAuthentication = docker.GetImageRegistryAuth(r, cfg.BuilderImage)
			}

			cfg.Environment = map[string]string{}

			if len(cfg.EnvironmentFile) > 0 {
				result, err := util.ReadEnvironmentFile(cfg.EnvironmentFile)
				if err != nil {
					glog.Warningf("Unable to read %s: %v", cfg.EnvironmentFile, err)
				} else {
					cfg.Environment = result
				}
			}

			envs, err := parseEnvs(cmd, "env")
			checkErr(err)
			for k, v := range envs {
				cfg.Environment[k] = v
			}

			if len(oldScriptsFlag) != 0 {
				glog.Warning("Flag --scripts is deprecated, use --scripts-url instead")
				cfg.ScriptsURL = oldScriptsFlag
			}
			if len(oldDestination) != 0 {
				glog.Warning("Flag --location is deprecated, use --destination instead")
				cfg.Destination = oldDestination
			}

			if glog.V(2) {
				fmt.Printf("\n%s\n", describe.DescribeConfig(cfg))
			}
			builder, err := strategies.GetStrategy(cfg)
			checkErr(err)
			result, err := builder.Build(cfg)
			checkErr(err)

			for _, message := range result.Messages {
				glog.V(1).Infof(message)
			}

		},
	}

	buildCmd.Flags().BoolVarP(&(cfg.Quiet), "quiet", "q", false, "Operate quietly. Suppress all non-error output.")
	buildCmd.Flags().BoolVar(&(cfg.Incremental), "incremental", false, "Perform an incremental build")
	buildCmd.Flags().BoolVar(&(cfg.RemovePreviousImage), "rm", false, "Remove the previous image during incremental builds")
	buildCmd.Flags().StringP("env", "e", "", "Specify an environment var NAME=VALUE,NAME2=VALUE2,...")
	buildCmd.Flags().StringVarP(&(cfg.Ref), "ref", "r", "", "Specify a ref to check-out")
	buildCmd.Flags().StringVar(&(cfg.CallbackURL), "callback-url", "", "Specify a URL to invoke via HTTP POST upon build completion")
	buildCmd.Flags().StringVarP(&(cfg.ScriptsURL), "scripts-url", "s", "", "Specify a URL for the assemble and run scripts")
	buildCmd.Flags().StringVar(&(oldScriptsFlag), "scripts", "", "Specify a URL for the assemble and run scripts")
	buildCmd.Flags().StringVarP(&(oldDestination), "location", "l", "", "Specify a destination location for untar operation")
	buildCmd.Flags().StringVarP(&(cfg.Destination), "destination", "d", "", "Specify a destination location for untar operation")
	buildCmd.Flags().BoolVar(&(cfg.ForcePull), "force-pull", true, "Always pull the builder image even if it is present locally")
	buildCmd.Flags().BoolVar(&(cfg.PreserveWorkingDir), "save-temp-dir", false, "Save the temporary directory used by STI instead of deleting it")
	buildCmd.Flags().BoolVar(&(useConfig), "use-config", false, "Store command line options to .stifile")
	buildCmd.Flags().StringVarP(&(cfg.ContextDir), "context-dir", "", "", "Specify the sub-directory inside the repository with the application sources")
	buildCmd.Flags().StringVarP(&(cfg.DockerCfgPath), "dockercfg-path", "", filepath.Join(os.Getenv("HOME"), ".dockercfg"), "Specify the path to the Docker configuration file")
	buildCmd.Flags().StringVarP(&(cfg.EnvironmentFile), "environment-file", "E", "", "Specify the path to the file with environment")
	buildCmd.Flags().StringVarP(&(cfg.DisplayName), "application-name", "n", "", "Specify the display name for the application (default: output image name)")
	buildCmd.Flags().StringVarP(&(cfg.Description), "description", "", "", "Specify the description of the application")

	return buildCmd
}
Ejemplo n.º 7
0
// Build executes STI build based on configured builder, S2I builder factory and S2I config validator
func (s *S2IBuilder) Build() error {
	var push bool

	contextDir := filepath.Clean(s.build.Spec.Source.ContextDir)
	if contextDir == "." || contextDir == "/" {
		contextDir = ""
	}
	buildDir, err := ioutil.TempDir("", "s2i-build")
	if err != nil {
		return err
	}
	srcDir := filepath.Join(buildDir, s2iapi.Source)
	if err := os.MkdirAll(srcDir, os.ModePerm); err != nil {
		return err
	}
	tmpDir := filepath.Join(buildDir, "tmp")
	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
		return err
	}

	download := &downloader{
		s:       s,
		in:      os.Stdin,
		timeout: urlCheckTimeout,

		dir:        srcDir,
		contextDir: contextDir,
		tmpDir:     tmpDir,
	}
	// if there is no output target, set one up so the docker build logic
	// (which requires a tag) will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Status.OutputDockerImageReference = s.build.Name
	} else {
		push = true
	}
	pushTag := s.build.Status.OutputDockerImageReference
	git := s.build.Spec.Source.Git

	var ref string
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		len(s.build.Spec.Revision.Git.Commit) != 0 {
		ref = s.build.Spec.Revision.Git.Commit
	} else if git != nil && len(git.Ref) != 0 {
		ref = git.Ref
	}

	sourceURI := &url.URL{
		Scheme:   "file",
		Path:     srcDir,
		Fragment: ref,
	}

	injections := s2iapi.InjectionList{}
	for _, s := range s.build.Spec.Source.Secrets {
		glog.V(3).Infof("Injecting secret %q into a build into %q", s.Secret.Name, filepath.Clean(s.DestinationDir))
		secretSourcePath := filepath.Join(strategy.SecretBuildSourceBaseMountPath, s.Secret.Name)
		injections = append(injections, s2iapi.InjectPath{
			SourcePath:     secretSourcePath,
			DestinationDir: s.DestinationDir,
		})
	}

	buildTag := randomBuildTag(s.build.Namespace, s.build.Name)

	config := &s2iapi.Config{
		WorkingDir:     buildDir,
		DockerConfig:   &s2iapi.DockerConfig{Endpoint: s.dockerSocket},
		DockerCfgPath:  os.Getenv(dockercfg.PullAuthType),
		LabelNamespace: api.DefaultDockerLabelNamespace,

		ScriptsURL: s.build.Spec.Strategy.SourceStrategy.Scripts,

		BuilderImage: s.build.Spec.Strategy.SourceStrategy.From.Name,
		Incremental:  s.build.Spec.Strategy.SourceStrategy.Incremental,

		Environment:       buildEnvVars(s.build),
		DockerNetworkMode: getDockerNetworkMode(),

		Source:       sourceURI.String(),
		Tag:          buildTag,
		ContextDir:   s.build.Spec.Source.ContextDir,
		CGroupLimits: s.cgLimits,
		Injections:   injections,
	}

	if s.build.Spec.Strategy.SourceStrategy.ForcePull {
		glog.V(4).Infof("With force pull true, setting policies to %s", s2iapi.PullAlways)
		config.PreviousImagePullPolicy = s2iapi.PullAlways
		config.BuilderPullPolicy = s2iapi.PullAlways
	} else {
		glog.V(4).Infof("With force pull false, setting policies to %s", s2iapi.PullIfNotPresent)
		config.PreviousImagePullPolicy = s2iapi.PullIfNotPresent
		config.BuilderPullPolicy = s2iapi.PullIfNotPresent
	}

	allowedUIDs := os.Getenv("ALLOWED_UIDS")
	glog.V(2).Infof("The value of ALLOWED_UIDS is [%s]", allowedUIDs)
	if len(allowedUIDs) > 0 {
		err := config.AllowedUIDs.Set(allowedUIDs)
		if err != nil {
			return err
		}
	}

	if errs := s.validator.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	config.PullAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType)
	config.IncrementalAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(pushTag, dockercfg.PushAuthType)

	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := s.builder.Builder(config, s2ibuild.Overrides{Downloader: download})
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	if _, err = builder.Build(config); err != nil {
		return err
	}

	cname := containerName("s2i", s.build.Name, s.build.Namespace, "post-commit")
	if err := execPostCommitHook(s.dockerClient, s.build.Spec.PostCommit, buildTag, cname); err != nil {
		return err
	}

	if push {
		if err := tagImage(s.dockerClient, buildTag, pushTag); err != nil {
			return err
		}
	}

	if err := removeImage(s.dockerClient, buildTag); err != nil {
		glog.Warningf("Failed to remove temporary build tag %v: %v", buildTag, err)
	}

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			pushTag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", pushTag)
		} else {
			glog.Infof("No push secret provided")
		}
		glog.Infof("Pushing %s image ...", pushTag)
		if err := pushImage(s.dockerClient, pushTag, pushAuthConfig); err != nil {
			// write extended error message to assist in problem resolution
			msg := fmt.Sprintf("Failed to push image. Response from registry is: %v", err)
			if authPresent {
				glog.Infof("Registry server Address: %s", pushAuthConfig.ServerAddress)
				glog.Infof("Registry server User Name: %s", pushAuthConfig.Username)
				glog.Infof("Registry server Email: %s", pushAuthConfig.Email)
				passwordPresent := "<<empty>>"
				if len(pushAuthConfig.Password) > 0 {
					passwordPresent = "<<non-empty>>"
				}
				glog.Infof("Registry server Password: %s", passwordPresent)
			}
			return errors.New(msg)
		}
		glog.Infof("Successfully pushed %s", pushTag)
		glog.Flush()
	}
	return nil
}
Ejemplo n.º 8
0
Archivo: sti.go Proyecto: mignev/origin
// Build executes the STI build
func (s *STIBuilder) Build() error {
	tag := s.build.Parameters.Output.DockerImageReference
	config := &stiapi.Config{
		BuilderImage:  s.build.Parameters.Strategy.SourceStrategy.From.Name,
		DockerConfig:  &stiapi.DockerConfig{Endpoint: s.dockerSocket},
		Source:        s.build.Parameters.Source.Git.URI,
		ContextDir:    s.build.Parameters.Source.ContextDir,
		DockerCfgPath: os.Getenv(dockercfg.PullAuthType),
		Tag:           tag,
		ScriptsURL:    s.build.Parameters.Strategy.SourceStrategy.Scripts,
		Environment:   getBuildEnvVars(s.build),
		Incremental:   s.build.Parameters.Strategy.SourceStrategy.Incremental,
	}

	if s.build.Parameters.Revision != nil && s.build.Parameters.Revision.Git != nil &&
		s.build.Parameters.Revision.Git.Commit != "" {
		config.Ref = s.build.Parameters.Revision.Git.Commit
	} else if s.build.Parameters.Source.Git.Ref != "" {
		config.Ref = s.build.Parameters.Source.Git.Ref
	}

	if errs := validation.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	if r, err := os.Open(config.DockerCfgPath); err == nil {
		config.PullAuthentication = stidocker.GetImageRegistryAuth(r, config.BuilderImage)
		glog.Infof("Using provided pull secret for pulling %s image", config.BuilderImage)
	}
	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := sti.GetStrategy(config)
	if err != nil {
		return err
	}
	defer removeImage(s.dockerClient, tag)
	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)
	if _, err = builder.Build(config); err != nil {
		return err
	}
	dockerImageRef := s.build.Parameters.Output.DockerImageReference
	if len(dockerImageRef) != 0 {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			dockerImageRef,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", dockerImageRef)
			s.auth = pushAuthConfig
		}
		glog.Infof("Pushing %s image ...", dockerImageRef)
		if err := pushImage(s.dockerClient, tag, s.auth); err != nil {
			return fmt.Errorf("Failed to push image: %v", err)
		}
		glog.Infof("Successfully pushed %s", dockerImageRef)
		glog.Flush()
	}
	return nil
}
Ejemplo n.º 9
0
// Build executes the STI build
func (s *STIBuilder) Build() error {
	var push bool

	// if there is no output target, set one up so the docker build logic
	// will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Spec.Output.To = &kapi.ObjectReference{
			Kind: "DockerImage",
			Name: noOutputDefaultTag,
		}
		push = false
	} else {
		push = true
	}
	tag := s.build.Spec.Output.To.Name

	config := &stiapi.Config{
		BuilderImage:  s.build.Spec.Strategy.SourceStrategy.From.Name,
		DockerConfig:  &stiapi.DockerConfig{Endpoint: s.dockerSocket},
		Source:        s.build.Spec.Source.Git.URI,
		ContextDir:    s.build.Spec.Source.ContextDir,
		DockerCfgPath: os.Getenv(dockercfg.PullAuthType),
		Tag:           tag,
		ScriptsURL:    s.build.Spec.Strategy.SourceStrategy.Scripts,
		Environment:   getBuildEnvVars(s.build),
		Incremental:   s.build.Spec.Strategy.SourceStrategy.Incremental,
		ForcePull:     s.build.Spec.Strategy.SourceStrategy.ForcePull,
	}
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		s.build.Spec.Revision.Git.Commit != "" {
		config.Ref = s.build.Spec.Revision.Git.Commit
	} else if s.build.Spec.Source.Git.Ref != "" {
		config.Ref = s.build.Spec.Source.Git.Ref
	}

	if errs := validation.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	// If DockerCfgPath is provided in api.Config, then attempt to read the the
	// dockercfg file and get the authentication for pulling the builder image.
	if r, err := os.Open(config.DockerCfgPath); err == nil {
		config.PullAuthentication = stidocker.GetImageRegistryAuth(r, config.BuilderImage)
		glog.Infof("Using provided pull secret for pulling %s image", config.BuilderImage)
	}
	glog.V(2).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := sti.GetStrategy(config)
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	origProxy := make(map[string]string)
	var setHttp, setHttps bool
	// set the http proxy to be used by the git clone performed by S2I
	if len(s.build.Spec.Source.Git.HTTPSProxy) != 0 {
		glog.V(2).Infof("Setting https proxy variables for Git to %s", s.build.Spec.Source.Git.HTTPSProxy)
		origProxy["HTTPS_PROXY"] = os.Getenv("HTTPS_PROXY")
		origProxy["https_proxy"] = os.Getenv("https_proxy")
		os.Setenv("HTTPS_PROXY", s.build.Spec.Source.Git.HTTPSProxy)
		os.Setenv("https_proxy", s.build.Spec.Source.Git.HTTPSProxy)
		setHttps = true
	}
	if len(s.build.Spec.Source.Git.HTTPProxy) != 0 {
		glog.V(2).Infof("Setting http proxy variables for Git to %s", s.build.Spec.Source.Git.HTTPSProxy)
		origProxy["HTTP_PROXY"] = os.Getenv("HTTP_PROXY")
		origProxy["http_proxy"] = os.Getenv("http_proxy")
		os.Setenv("HTTP_PROXY", s.build.Spec.Source.Git.HTTPProxy)
		os.Setenv("http_proxy", s.build.Spec.Source.Git.HTTPProxy)
		setHttp = true
	}

	if _, err = builder.Build(config); err != nil {
		return err
	}

	// reset http proxy env variables to original value
	if setHttps {
		glog.V(4).Infof("Resetting HTTPS_PROXY variable for Git to %s", origProxy["HTTPS_PROXY"])
		os.Setenv("HTTPS_PROXY", origProxy["HTTPS_PROXY"])
		glog.V(4).Infof("Resetting https_proxy variable for Git to %s", origProxy["https_proxy"])
		os.Setenv("https_proxy", origProxy["https_proxy"])
	}
	if setHttp {
		glog.V(4).Infof("Resetting HTTP_PROXY variable for Git to %s", origProxy["HTTP_PROXY"])
		os.Setenv("HTTP_PROXY", origProxy["HTTP_PROXY"])
		glog.V(4).Infof("Resetting http_proxy variable for Git to %s", origProxy["http_proxy"])
		os.Setenv("http_proxy", origProxy["http_proxy"])
	}

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			tag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.Infof("Using provided push secret for pushing %s image", tag)
			s.auth = pushAuthConfig
		}
		glog.Infof("Pushing %s image ...", tag)
		if err := pushImage(s.dockerClient, tag, s.auth); err != nil {
			return fmt.Errorf("Failed to push image: %v", err)
		}
		glog.Infof("Successfully pushed %s", tag)
		glog.Flush()
	}
	return nil
}
Ejemplo n.º 10
0
// Build executes STI build based on configured builder, S2I builder factory and S2I config validator
func (s *S2IBuilder) Build() error {
	if s.build.Spec.Strategy.SourceStrategy == nil {
		return errors.New("the source to image builder must be used with the source strategy")
	}

	var push bool

	contextDir := filepath.Clean(s.build.Spec.Source.ContextDir)
	if contextDir == "." || contextDir == "/" {
		contextDir = ""
	}
	buildDir, err := ioutil.TempDir("", "s2i-build")
	if err != nil {
		return err
	}
	srcDir := filepath.Join(buildDir, s2iapi.Source)
	if err := os.MkdirAll(srcDir, os.ModePerm); err != nil {
		return err
	}
	tmpDir := filepath.Join(buildDir, "tmp")
	if err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {
		return err
	}

	download := &downloader{
		s:       s,
		in:      os.Stdin,
		timeout: initialURLCheckTimeout,

		dir:        srcDir,
		contextDir: contextDir,
		tmpDir:     tmpDir,
	}
	// if there is no output target, set one up so the docker build logic
	// (which requires a tag) will still work, but we won't push it at the end.
	if s.build.Spec.Output.To == nil || len(s.build.Spec.Output.To.Name) == 0 {
		s.build.Status.OutputDockerImageReference = s.build.Name
	} else {
		push = true
	}
	pushTag := s.build.Status.OutputDockerImageReference
	git := s.build.Spec.Source.Git

	var ref string
	if s.build.Spec.Revision != nil && s.build.Spec.Revision.Git != nil &&
		len(s.build.Spec.Revision.Git.Commit) != 0 {
		ref = s.build.Spec.Revision.Git.Commit
	} else if git != nil && len(git.Ref) != 0 {
		ref = git.Ref
	}

	sourceURI := &url.URL{
		Scheme:   "file",
		Path:     srcDir,
		Fragment: ref,
	}

	injections := s2iapi.VolumeList{}
	for _, s := range s.build.Spec.Source.Secrets {
		glog.V(3).Infof("Injecting secret %q into a build into %q", s.Secret.Name, filepath.Clean(s.DestinationDir))
		secretSourcePath := filepath.Join(strategy.SecretBuildSourceBaseMountPath, s.Secret.Name)
		injections = append(injections, s2iapi.VolumeSpec{
			Source:      secretSourcePath,
			Destination: s.DestinationDir,
		})
	}

	buildTag := randomBuildTag(s.build.Namespace, s.build.Name)
	scriptDownloadProxyConfig, err := scriptProxyConfig(s.build)
	if err != nil {
		return err
	}
	if scriptDownloadProxyConfig != nil {
		glog.V(0).Infof("Using HTTP proxy %v and HTTPS proxy %v for script download",
			scriptDownloadProxyConfig.HTTPProxy,
			scriptDownloadProxyConfig.HTTPSProxy)
	}

	var incremental bool
	if s.build.Spec.Strategy.SourceStrategy.Incremental != nil {
		incremental = *s.build.Spec.Strategy.SourceStrategy.Incremental
	}
	config := &s2iapi.Config{
		WorkingDir:     buildDir,
		DockerConfig:   &s2iapi.DockerConfig{Endpoint: s.dockerSocket},
		DockerCfgPath:  os.Getenv(dockercfg.PullAuthType),
		LabelNamespace: api.DefaultDockerLabelNamespace,

		ScriptsURL: s.build.Spec.Strategy.SourceStrategy.Scripts,

		BuilderImage:       s.build.Spec.Strategy.SourceStrategy.From.Name,
		Incremental:        incremental,
		IncrementalFromTag: pushTag,

		Environment:       buildEnvVars(s.build),
		DockerNetworkMode: getDockerNetworkMode(),

		Source:                    sourceURI.String(),
		Tag:                       buildTag,
		ContextDir:                s.build.Spec.Source.ContextDir,
		CGroupLimits:              s.cgLimits,
		Injections:                injections,
		ScriptDownloadProxyConfig: scriptDownloadProxyConfig,
		BlockOnBuild:              true,
	}

	if s.build.Spec.Strategy.SourceStrategy.ForcePull {
		glog.V(4).Infof("With force pull true, setting policies to %s", s2iapi.PullAlways)
		config.BuilderPullPolicy = s2iapi.PullAlways
		config.RuntimeImagePullPolicy = s2iapi.PullAlways
	} else {
		glog.V(4).Infof("With force pull false, setting policies to %s", s2iapi.PullIfNotPresent)
		config.BuilderPullPolicy = s2iapi.PullIfNotPresent
		config.RuntimeImagePullPolicy = s2iapi.PullIfNotPresent
	}
	config.PreviousImagePullPolicy = s2iapi.PullAlways

	allowedUIDs := os.Getenv(api.AllowedUIDs)
	glog.V(4).Infof("The value of %s is [%s]", api.AllowedUIDs, allowedUIDs)
	if len(allowedUIDs) > 0 {
		err := config.AllowedUIDs.Set(allowedUIDs)
		if err != nil {
			return err
		}
	}
	dropCaps := os.Getenv(api.DropCapabilities)
	glog.V(4).Infof("The value of %s is [%s]", api.DropCapabilities, dropCaps)
	if len(dropCaps) > 0 {
		config.DropCapabilities = strings.Split(dropCaps, ",")
	}

	if s.build.Spec.Strategy.SourceStrategy.RuntimeImage != nil {
		runtimeImageName := s.build.Spec.Strategy.SourceStrategy.RuntimeImage.Name
		config.RuntimeImage = runtimeImageName
		config.RuntimeAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(runtimeImageName, dockercfg.PullAuthType)
		config.RuntimeArtifacts = copyToVolumeList(s.build.Spec.Strategy.SourceStrategy.RuntimeArtifacts)
	}
	// If DockerCfgPath is provided in api.Config, then attempt to read the
	// dockercfg file and get the authentication for pulling the builder image.
	config.PullAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType)
	config.IncrementalAuthentication, _ = dockercfg.NewHelper().GetDockerAuth(pushTag, dockercfg.PushAuthType)

	if errs := s.validator.ValidateConfig(config); len(errs) != 0 {
		var buffer bytes.Buffer
		for _, ve := range errs {
			buffer.WriteString(ve.Error())
			buffer.WriteString(", ")
		}
		return errors.New(buffer.String())
	}

	glog.V(4).Infof("Creating a new S2I builder with build config: %#v\n", describe.DescribeConfig(config))
	builder, err := s.builder.Builder(config, s2ibuild.Overrides{Downloader: download})
	if err != nil {
		return err
	}

	glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name)

	if _, err = builder.Build(config); err != nil {
		return err
	}

	cname := containerName("s2i", s.build.Name, s.build.Namespace, "post-commit")
	if err := execPostCommitHook(s.dockerClient, s.build.Spec.PostCommit, buildTag, cname); err != nil {
		return err
	}

	if push {
		if err := tagImage(s.dockerClient, buildTag, pushTag); err != nil {
			return err
		}
	}

	if err := removeImage(s.dockerClient, buildTag); err != nil {
		glog.V(0).Infof("warning: Failed to remove temporary build tag %v: %v", buildTag, err)
	}

	if push {
		// Get the Docker push authentication
		pushAuthConfig, authPresent := dockercfg.NewHelper().GetDockerAuth(
			pushTag,
			dockercfg.PushAuthType,
		)
		if authPresent {
			glog.V(3).Infof("Using provided push secret for pushing %s image", pushTag)
		} else {
			glog.V(3).Infof("No push secret provided")
		}
		glog.V(0).Infof("\nPushing image %s ...", pushTag)
		if err := pushImage(s.dockerClient, pushTag, pushAuthConfig); err != nil {
			return reportPushFailure(err, authPresent, pushAuthConfig)
		}
		glog.V(0).Infof("Push successful")
	}
	return nil
}
Ejemplo n.º 11
0
func newCmdBuild(cfg *api.Config) *cobra.Command {
	useConfig := false
	oldScriptsFlag := ""
	oldDestination := ""

	buildCmd := &cobra.Command{
		Use:   "build <source> <image> [<tag>]",
		Short: "Build a new image",
		Long:  "Build a new Docker image named <tag> (if provided) from a source repository and base image.",
		Example: `
# Build an application Docker image from a Git repository
$ s2i build git://github.com/openshift/ruby-hello-world centos/ruby-22-centos7 hello-world-app

# Build from a local directory
$ s2i build . centos/ruby-22-centos7 hello-world-app
`,
		Run: func(cmd *cobra.Command, args []string) {
			if glog.V(1) {
				glog.Infof("Running S2I version %q\n", version.Get())
			}

			// Attempt to restore the build command from the configuration file
			if useConfig {
				config.Restore(cfg, cmd)
			}

			// If user specifies the arguments, then we override the stored ones
			if len(args) >= 2 {
				cfg.Source = args[0]
				cfg.BuilderImage = args[1]
				if len(args) >= 3 {
					cfg.Tag = args[2]
				}
			}

			if cfg.ForcePull {
				glog.Warning("DEPRECATED: The '--force-pull' option is deprecated. Use '--pull-policy' instead")
			}

			if len(cfg.BuilderPullPolicy) == 0 {
				cfg.BuilderPullPolicy = api.DefaultBuilderPullPolicy
			}
			if len(cfg.PreviousImagePullPolicy) == 0 {
				cfg.PreviousImagePullPolicy = api.DefaultPreviousImagePullPolicy
			}

			if errs := validation.ValidateConfig(cfg); len(errs) > 0 {
				for _, e := range errs {
					fmt.Fprintf(os.Stderr, "ERROR: %s\n", e)
				}
				fmt.Println()
				cmd.Help()
				os.Exit(1)
			}

			// Persists the current command line options and config into .s2ifile
			if useConfig {
				config.Save(cfg, cmd)
			}

			// Attempt to read the .dockercfg and extract the authentication for
			// docker pull
			if r, err := os.Open(cfg.DockerCfgPath); err == nil {
				defer r.Close()
				auths := docker.LoadImageRegistryAuth(r)
				cfg.PullAuthentication = docker.GetImageRegistryAuth(auths, cfg.BuilderImage)
				if cfg.Incremental {
					cfg.IncrementalAuthentication = docker.GetImageRegistryAuth(auths, cfg.Tag)
				}
			}

			if len(cfg.EnvironmentFile) > 0 {
				result, err := util.ReadEnvironmentFile(cfg.EnvironmentFile)
				if err != nil {
					glog.Warningf("Unable to read environment file %q: %v", cfg.EnvironmentFile, err)
				} else {
					for name, value := range result {
						cfg.Environment = append(cfg.Environment, api.EnvironmentSpec{Name: name, Value: value})
					}
				}
			}

			if len(oldScriptsFlag) != 0 {
				glog.Warning("DEPRECATED: Flag --scripts is deprecated, use --scripts-url instead")
				cfg.ScriptsURL = oldScriptsFlag
			}
			if len(oldDestination) != 0 {
				glog.Warning("DEPRECATED: Flag --location is deprecated, use --destination instead")
				cfg.Destination = oldDestination
			}

			if glog.V(2) {
				fmt.Printf("\n%s\n", describe.DescribeConfig(cfg))
			}

			if !docker.IsReachable(cfg) {
				glog.Fatalf("Unable to connect to Docker daemon. Please set the DOCKER_HOST or make sure the Docker socket %q exists", cfg.DockerConfig.Endpoint)
			}

			builder, err := strategies.GetStrategy(cfg)
			checkErr(err)
			result, err := builder.Build(cfg)
			checkErr(err)

			for _, message := range result.Messages {
				glog.V(1).Infof(message)
			}

			if cfg.RunImage {
				runner, err := run.New(cfg)
				checkErr(err)
				err = runner.Run(cfg)
				checkErr(err)
			}

		},
	}

	cmdutil.AddCommonFlags(buildCmd, cfg)

	buildCmd.Flags().BoolVar(&(cfg.RunImage), "run", false, "Run resulting image as part of invocation of this command")
	buildCmd.Flags().BoolVar(&(cfg.DisableRecursive), "recursive", true, "Fetch all git submodules when cloning application repository")
	buildCmd.Flags().VarP(&(cfg.Environment), "env", "e", "Specify an single environment variable in NAME=VALUE format")
	buildCmd.Flags().StringVarP(&(cfg.Ref), "ref", "r", "", "Specify a ref to check-out")
	buildCmd.Flags().StringVarP(&(cfg.AssembleUser), "assemble-user", "", "", "Specify the user to run assemble with")
	buildCmd.Flags().StringVarP(&(cfg.ContextDir), "context-dir", "", "", "Specify the sub-directory inside the repository with the application sources")
	buildCmd.Flags().StringVarP(&(cfg.ExcludeRegExp), "exclude", "", tar.DefaultExclusionPattern.String(), "Regular expression for selecting files from the source tree to exclude from the build, where the default excludes the '.git' directory (see https://golang.org/pkg/regexp for syntax, but note that \"\" will be interpreted as allow all files and exclude no files)")
	buildCmd.Flags().StringVarP(&(cfg.ScriptsURL), "scripts-url", "s", "", "Specify a URL for the assemble and run scripts")
	buildCmd.Flags().StringVar(&(oldScriptsFlag), "scripts", "", "DEPRECATED: Specify a URL for the assemble and run scripts")
	buildCmd.Flags().BoolVar(&(useConfig), "use-config", false, "Store command line options to .s2ifile")
	buildCmd.Flags().StringVarP(&(cfg.EnvironmentFile), "environment-file", "E", "", "Specify the path to the file with environment")
	buildCmd.Flags().StringVarP(&(cfg.DisplayName), "application-name", "n", "", "Specify the display name for the application (default: output image name)")
	buildCmd.Flags().StringVarP(&(cfg.Description), "description", "", "", "Specify the description of the application")
	buildCmd.Flags().VarP(&(cfg.AllowedUIDs), "allowed-uids", "u", "Specify a range of allowed user ids for the builder image")
	buildCmd.Flags().VarP(&(cfg.Injections), "inject", "i", "Specify a directory to inject into the assemble container")
	buildCmd.Flags().VarP(&(cfg.BuildVolumes), "volume", "v", "Specify a volume to mount into the assemble container")
	buildCmd.Flags().StringSliceVar(&(cfg.DropCapabilities), "cap-drop", []string{}, "Specify a comma-separated list of capabilities to drop when running Docker containers")
	buildCmd.Flags().StringVarP(&(oldDestination), "location", "l", "",
		"DEPRECATED: Specify a destination location for untar operation")
	buildCmd.Flags().BoolVarP(&(cfg.ForceCopy), "copy", "c", false, "Use local file system copy instead of git cloning the source url")

	return buildCmd
}