277a3984 |
package cmd
import ( |
fe7039fe |
"bytes" |
277a3984 |
"fmt"
"io" |
1b9f255f |
"io/ioutil" |
277a3984 |
"os"
|
b36f0965 |
"github.com/MakeNowJust/heredoc" |
277a3984 |
"github.com/spf13/cobra" |
e8bc19d6 |
|
95ec120f |
kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" |
04990ffb |
"k8s.io/kubernetes/pkg/runtime" |
83c702b4 |
"k8s.io/kubernetes/pkg/util/errors" |
277a3984 |
buildapi "github.com/openshift/origin/pkg/build/api" |
6267dded |
"github.com/openshift/origin/pkg/cmd/templates" |
95ec120f |
cmdutil "github.com/openshift/origin/pkg/cmd/util" |
277a3984 |
"github.com/openshift/origin/pkg/cmd/util/clientcmd" |
93f4af33 |
configcmd "github.com/openshift/origin/pkg/config/cmd" |
98cd35ba |
newapp "github.com/openshift/origin/pkg/generate/app" |
277a3984 |
newcmd "github.com/openshift/origin/pkg/generate/app/cmd"
)
|
64fed1a6 |
// NewBuildRecommendedCommandName is the recommended command name.
const NewBuildRecommendedCommandName = "new-build"
|
6267dded |
var (
newBuildLong = templates.LongDesc(`
Create a new build by specifying source code |
277a3984 |
|
6267dded |
This command will try to create a build configuration for your application using images and
code that has a public repository. It will lookup the images on the local Docker installation
(if available), a Docker registry, or an image stream. |
c3e1dbba |
|
6267dded |
If you specify a source code URL, it will set up a build that takes your source code and converts
it into an image that can run inside of a pod. Local source must be in a git repository that has a
remote repository that the server can see. |
277a3984 |
|
6267dded |
Once the build configuration is created a new build will be automatically triggered.
You can use '%[1]s status' to check the progress.`) |
277a3984 |
|
6267dded |
newBuildExample = templates.Examples(`
# Create a build config based on the source code in the current git repository (with a public
# remote) and a Docker image
%[1]s %[2]s . --docker-image=repo/langimage |
277a3984 |
|
6267dded |
# Create a NodeJS build config based on the provided [image]~[source code] combination
%[1]s %[2]s openshift/nodejs-010-centos7~https://github.com/openshift/nodejs-ex.git |
277a3984 |
|
6267dded |
# Create a build config from a remote repository using its beta2 branch
%[1]s %[2]s https://github.com/openshift/ruby-hello-world#beta2 |
4ef01b69 |
|
6267dded |
# Create a build config using a Dockerfile specified as an argument
%[1]s %[2]s -D $'FROM centos:7\nRUN yum install -y httpd' |
1b9f255f |
|
6267dded |
# Create a build config from a remote repository and add custom environment variables
%[1]s %[2]s https://github.com/openshift/ruby-hello-world RACK_ENV=development |
04e96a3a |
|
6267dded |
# Create a build config from a remote repository and inject the npmrc into a build
%[1]s %[2]s https://github.com/openshift/ruby-hello-world --build-secret npmrc:.npmrc |
a166f955 |
|
6267dded |
# Create a build config that gets its input from a remote repository and another Docker image
%[1]s %[2]s https://github.com/openshift/ruby-hello-world --source-image=openshift/jenkins-1-centos7 --source-image-path=/var/lib/jenkins:tmp`) |
98cd35ba |
newBuildNoInput = `You must specify one or more images, image streams, or source code locations to create a build.
To build from an existing image stream tag or Docker image, provide the name of the image and
the source code location:
|
64fed1a6 |
%[1]s %[2]s openshift/nodejs-010-centos7~https://github.com/openshift/nodejs-ex.git |
98cd35ba |
If you only specify the source repository location (local or remote), the command will look at
the repo to determine the type, and then look for a matching image on your server or on the
default Docker registry.
|
64fed1a6 |
%[1]s %[2]s https://github.com/openshift/nodejs-ex.git |
98cd35ba |
will look for an image called "nodejs" in your current project, the 'openshift' project, or
on the Docker Hub.
` |
277a3984 |
)
|
04990ffb |
type NewBuildOptions struct { |
9cf65112 |
Action configcmd.BulkAction |
04990ffb |
Config *newcmd.AppConfig
|
64fed1a6 |
BaseName string |
04990ffb |
CommandPath string
CommandName string
Out, ErrOut io.Writer
Output string
PrintObject func(obj runtime.Object) error
LogsForObject LogsForObjectFunc
}
|
277a3984 |
// NewCmdNewBuild implements the OpenShift cli new-build command |
dd473fd9 |
func NewCmdNewBuild(name, baseName string, f *clientcmd.Factory, in io.Reader, out, errout io.Writer) *cobra.Command { |
1d678d0c |
config := newcmd.NewAppConfig() |
f58fde40 |
config.ExpectToBuild = true |
04990ffb |
config.AddEnvironmentToBuild = true |
64fed1a6 |
o := &NewBuildOptions{Config: config} |
277a3984 |
cmd := &cobra.Command{ |
64fed1a6 |
Use: fmt.Sprintf("%s (IMAGE | IMAGESTREAM | PATH | URL ...)", name), |
b3e77d8d |
Short: "Create a new build configuration", |
64fed1a6 |
Long: fmt.Sprintf(newBuildLong, baseName, name),
Example: fmt.Sprintf(newBuildExample, baseName, name), |
b3e77d8d |
SuggestFor: []string{"build", "builds"}, |
277a3984 |
Run: func(c *cobra.Command, args []string) { |
dd473fd9 |
kcmdutil.CheckErr(o.Complete(baseName, name, f, c, args, out, errout, in)) |
64fed1a6 |
err := o.RunNewBuild() |
95ec120f |
if err == cmdutil.ErrExit { |
277a3984 |
os.Exit(1)
} |
95ec120f |
kcmdutil.CheckErr(err) |
277a3984 |
},
}
|
5997bb61 |
cmd.Flags().StringSliceVar(&config.SourceRepositories, "code", config.SourceRepositories, "Source code in the build configuration.") |
6517d767 |
cmd.Flags().StringSliceVarP(&config.ImageStreams, "image", "", config.ImageStreams, "Name of an image stream to to use as a builder. (deprecated)")
cmd.Flags().MarkDeprecated("image", "use --image-stream instead")
cmd.Flags().StringSliceVarP(&config.ImageStreams, "image-stream", "i", config.ImageStreams, "Name of an image stream to to use as a builder.") |
5997bb61 |
cmd.Flags().StringSliceVar(&config.DockerImages, "docker-image", config.DockerImages, "Name of a Docker image to use as a builder.") |
04e96a3a |
cmd.Flags().StringSliceVar(&config.Secrets, "build-secret", config.Secrets, "Secret and destination to use as an input for the build.") |
57747fd5 |
cmd.Flags().StringVar(&config.Name, "name", "", "Set name to use for generated build artifacts.")
cmd.Flags().StringVar(&config.To, "to", "", "Push built images to this image stream tag (or Docker image repository if --to-docker is set).")
cmd.Flags().BoolVar(&config.OutputDocker, "to-docker", false, "Have the build output push to a Docker repository.") |
5997bb61 |
cmd.Flags().StringSliceVarP(&config.Environment, "env", "e", config.Environment, "Specify key value pairs of environment variables to set into resulting image.") |
4b35f1a0 |
cmd.Flags().StringVar(&config.Strategy, "strategy", "", "Specify the build strategy to use if you don't want to detect (docker|source).") |
1b9f255f |
cmd.Flags().StringVarP(&config.Dockerfile, "dockerfile", "D", "", "Specify the contents of a Dockerfile to build directly, implies --strategy=docker. Pass '-' to read from STDIN.") |
f58fde40 |
cmd.Flags().BoolVar(&config.BinaryBuild, "binary", false, "Instead of expecting a source URL, set the build to expect binary contents. Will disable triggers.") |
277a3984 |
cmd.Flags().StringP("labels", "l", "", "Label to set in all generated resources.") |
c6bc5ea9 |
cmd.Flags().BoolVar(&config.AllowMissingImages, "allow-missing-images", false, "If true, indicates that referenced Docker images that cannot be found locally or in a registry should still be used.") |
0fd621f4 |
cmd.Flags().BoolVar(&config.AllowMissingImageStreamTags, "allow-missing-imagestream-tags", false, "If true, indicates that image stream tags that don't exist should still be used.") |
ce45941e |
cmd.Flags().StringVar(&config.ContextDir, "context-dir", "", "Context directory to be used for the build.") |
96fd8c26 |
cmd.Flags().BoolVar(&config.NoOutput, "no-output", false, "If true, the build output will not be pushed anywhere.") |
fecf2d71 |
cmd.Flags().StringVar(&config.SourceImage, "source-image", "", "Specify an image to use as source for the build. You must also specify --source-image-path.")
cmd.Flags().StringVar(&config.SourceImagePath, "source-image-path", "", "Specify the file or directory to copy from the source image and its destination in the build directory. Format: [source]:[destination-dir].") |
9cf65112 |
|
64fed1a6 |
o.Action.BindForOutput(cmd.Flags()) |
9cf65112 |
cmd.Flags().String("output-version", "", "The preferred API versions of the output objects") |
277a3984 |
return cmd
}
|
04990ffb |
// Complete sets any default behavior for the command |
dd473fd9 |
func (o *NewBuildOptions) Complete(baseName, commandName string, f *clientcmd.Factory, c *cobra.Command, args []string, out, errout io.Writer, in io.Reader) error { |
04990ffb |
o.Out = out |
dd473fd9 |
o.ErrOut = errout |
04990ffb |
o.Output = kcmdutil.GetFlagString(c, "output")
// Only output="" should print descriptions of intermediate steps. Everything
// else should print only some specific output (json, yaml, go-template, ...)
if len(o.Output) == 0 {
o.Config.Out = o.Out
} else {
o.Config.Out = ioutil.Discard
}
o.Config.ErrOut = o.ErrOut |
a680572a |
|
9cf65112 |
o.Action.Out, o.Action.ErrOut = o.Out, o.ErrOut
o.Action.Bulk.Mapper = clientcmd.ResourceMapper(f)
o.Action.Bulk.Op = configcmd.Create
// Retry is used to support previous versions of the API server that will
// consider the presence of an unknown trigger type to be an error.
o.Action.Bulk.Retry = retryBuildConfig
o.Config.DryRun = o.Action.DryRun
|
64fed1a6 |
o.BaseName = baseName |
04990ffb |
o.CommandPath = c.CommandPath() |
64fed1a6 |
o.CommandName = commandName
|
59e6f9d2 |
mapper, _ := f.Object(false)
o.PrintObject = cmdutil.VersionedPrintObject(f.PrintObject, c, mapper, out) |
04990ffb |
o.LogsForObject = f.LogsForObject
if err := CompleteAppConfig(o.Config, f, c, args); err != nil {
return err
}
if o.Config.Dockerfile == "-" { |
1b9f255f |
data, err := ioutil.ReadAll(in)
if err != nil {
return err
} |
04990ffb |
o.Config.Dockerfile = string(data) |
1b9f255f |
} |
04990ffb |
if err := setAppConfigLabels(c, o.Config); err != nil { |
277a3984 |
return err
} |
04990ffb |
return nil
}
|
64fed1a6 |
// RunNewBuild contains all the necessary functionality for the OpenShift cli new-build command
func (o *NewBuildOptions) RunNewBuild() error { |
04990ffb |
config := o.Config
out := o.Out |
277a3984 |
|
0b6008ee |
checkGitInstalled(out)
|
93f4af33 |
result, err := config.Run() |
277a3984 |
if err != nil { |
64fed1a6 |
return handleBuildError(err, o.BaseName, o.CommandName, o.CommandPath) |
277a3984 |
} |
93f4af33 |
if len(config.Labels) == 0 && len(result.Name) > 0 {
config.Labels = map[string]string{"build": result.Name}
}
|
d27713ef |
if err := setLabels(config.Labels, result); err != nil { |
277a3984 |
return err
} |
7aa66f3e |
if err := setAnnotations(map[string]string{newcmd.GeneratedByNamespace: newcmd.GeneratedByNewBuild}, result); err != nil {
return err
} |
93f4af33 |
|
9cf65112 |
if o.Action.ShouldPrint() { |
04990ffb |
return o.PrintObject(result.List) |
93f4af33 |
}
|
9cf65112 |
if errs := o.Action.WithMessage(configcmd.CreateMessage(config.Labels), "created").Run(result.List, result.Namespace); len(errs) > 0 {
return cmdutil.ErrExit |
277a3984 |
}
|
9cf65112 |
if !o.Action.Verbose() || o.Action.DryRun { |
93f4af33 |
return nil
}
|
9cf65112 |
indent := o.Action.DefaultIndent() |
277a3984 |
for _, item := range result.List.Items {
switch t := item.(type) {
case *buildapi.BuildConfig: |
5bf62487 |
if len(t.Spec.Triggers) > 0 && t.Spec.Source.Binary == nil {
fmt.Fprintf(out, "%sBuild configuration %q created and build triggered.\n", indent, t.Name) |
04990ffb |
fmt.Fprintf(out, "%sRun '%s logs -f bc/%s' to stream the build progress.\n", indent, o.CommandName, t.Name) |
5bf62487 |
} |
277a3984 |
}
}
return nil
} |
98cd35ba |
|
64fed1a6 |
func handleBuildError(err error, baseName, commandName, commandPath string) error { |
98cd35ba |
if err == nil {
return nil
} |
fe7039fe |
errs := []error{err}
if agg, ok := err.(errors.Aggregate); ok {
errs = agg.Errors()
}
groups := errorGroups{}
for _, err := range errs { |
64fed1a6 |
transformBuildError(err, baseName, commandName, commandPath, groups) |
fe7039fe |
}
buf := &bytes.Buffer{}
for _, group := range groups { |
95ec120f |
fmt.Fprint(buf, kcmdutil.MultipleErrors("error: ", group.errs)) |
fe7039fe |
if len(group.suggestion) > 0 {
fmt.Fprintln(buf) |
98cd35ba |
} |
fe7039fe |
fmt.Fprint(buf, group.suggestion) |
98cd35ba |
} |
fe7039fe |
return fmt.Errorf(buf.String())
}
|
64fed1a6 |
func transformBuildError(err error, baseName, commandName, commandPath string, groups errorGroups) { |
98cd35ba |
switch t := err.(type) {
case newapp.ErrNoMatch: |
fe7039fe |
groups.Add(
"no-matches", |
b36f0965 |
heredoc.Docf(` |
fe7039fe |
The '%[1]s' command will match arguments to the following types:
1. Images tagged into image streams in the current project or the 'openshift' project
- if you don't specify a tag, we'll add ':latest'
2. Images in the Docker Hub, on remote registries, or on the local Docker engine
3. Git repository URLs or local paths that point to Git repositories
|
bf2eaf23 |
--allow-missing-images can be used to force the use of an image that was not matched |
fe7039fe |
|
04990ffb |
See '%[1]s -h' for examples.`, commandPath, |
fe7039fe |
),
t,
t.Errs...,
)
return
} |
1acc3c75 |
switch err {
case newcmd.ErrNoInputs: |
64fed1a6 |
groups.Add("", "", usageError(commandPath, newBuildNoInput, baseName, commandName)) |
1acc3c75 |
return
} |
64fed1a6 |
transformError(err, baseName, commandName, commandPath, groups) |
98cd35ba |
} |