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) } pr, err := docker.GetRebuildImage(cfg) checkErr(err) err = build.GenerateConfigFromLabels(cfg, pr) 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 } glog.V(2).Infof("\n%s\n", describe.Config(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 }
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) { glog.V(1).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.Incremental && len(cfg.RuntimeImage) > 0 { fmt.Fprintln(os.Stderr, "ERROR: Incremental build with runtime image isn't supported") os.Exit(1) } 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.RuntimeImage) > 0 { cfg.RuntimeAuthentication = docker.GetImageRegistryAuth(auths, cfg.RuntimeImage) } } 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 } glog.V(2).Infof("\n%s\n", describe.Config(cfg)) err := docker.CheckReachable(cfg) if err != nil { glog.Fatal(err) } 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.IgnoreSubmodules), "ignore-submodules", false, "Ignore 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, assemble-runtime 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 and runtime images") 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") buildCmd.Flags().StringVar(&(cfg.RuntimeImage), "runtime-image", "", "Image that will be used as the base for the runtime image") buildCmd.Flags().VarP(&(cfg.RuntimeArtifacts), "runtime-artifact", "a", "Specify a file or directory to be copied from the builder to the runtime image") return buildCmd }
// 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") } 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, } 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.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), Labels: buildLabels(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 t, _ := dockercfg.NewHelper().GetDockerAuth(runtimeImageName, dockercfg.PullAuthType) config.RuntimeAuthentication = s2iapi.AuthConfig{Username: t.Username, Password: t.Password, Email: t.Email, ServerAddress: t.ServerAddress} 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. t, _ := dockercfg.NewHelper().GetDockerAuth(config.BuilderImage, dockercfg.PullAuthType) config.PullAuthentication = s2iapi.AuthConfig{Username: t.Username, Password: t.Password, Email: t.Email, ServerAddress: t.ServerAddress} t, _ = dockercfg.NewHelper().GetDockerAuth(pushTag, dockercfg.PushAuthType) config.IncrementalAuthentication = s2iapi.AuthConfig{Username: t.Username, Password: t.Password, Email: t.Email, ServerAddress: t.ServerAddress} 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.Config(config)) builder, buildInfo, err := s.builder.Builder(config, s2ibuild.Overrides{Downloader: download}) if err != nil { s.build.Status.Reason, s.build.Status.Message = convertS2IFailureType(buildInfo.FailureReason.Reason, buildInfo.FailureReason.Message) if updateErr := retryBuildStatusUpdate(s.build, s.client, nil); updateErr != nil { utilruntime.HandleError(fmt.Errorf("error: An error occured while updating the build status: %v", updateErr)) } return err } glog.V(4).Infof("Starting S2I build from %s/%s BuildConfig ...", s.build.Namespace, s.build.Name) result, err := builder.Build(config) if err != nil { s.build.Status.Reason, s.build.Status.Message = convertS2IFailureType(result.BuildInfo.FailureReason.Reason, result.BuildInfo.FailureReason.Message) if updateErr := retryBuildStatusUpdate(s.build, s.client, nil); updateErr != nil { utilruntime.HandleError(fmt.Errorf("error: An error occured while updating the build status: %v", updateErr)) } 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 { s.build.Status.Reason = api.StatusReasonPostCommitHookFailed s.build.Status.Message = api.StatusMessagePostCommitHookFailed if updateErr := retryBuildStatusUpdate(s.build, s.client, nil); updateErr != nil { utilruntime.HandleError(fmt.Errorf("error: An error occured while updating the build status: %v", updateErr)) } 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 { s.build.Status.Reason = api.StatusReasonPushImageToRegistryFailed s.build.Status.Message = api.StatusMessagePushImageToRegistryFailed if updateErr := retryBuildStatusUpdate(s.build, s.client, nil); updateErr != nil { utilruntime.HandleError(fmt.Errorf("error: An error occured while updating the build status: %v", updateErr)) } return reportPushFailure(err, authPresent, pushAuthConfig) } glog.V(0).Infof("Push successful") } return nil }