From 677a1041359117f652ae8b8a05ec4b2e0309073a Mon Sep 17 00:00:00 2001 From: Igor Sutton Lopes Date: Fri, 10 Jan 2020 10:51:46 +0100 Subject: [PATCH] Add generate command This change adds the generate command to s2i, where it allows the user to produce a Dockerfile able to be used by any build system supporting the format, such as buildah and possibly others. --- contrib/completions/bash/s2i | 36 +++++++ contrib/completions/zsh/s2i | 36 +++++++ pkg/build/strategies/dockerfile/dockerfile.go | 9 ++ pkg/cmd/cli/cli.go | 1 + pkg/cmd/cli/cmd/generate.go | 99 +++++++++++++++++++ test/integration/docker/integration_test.go | 2 +- .../integration/dockerfile/dockerfile_test.go | 6 +- 7 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 pkg/cmd/cli/cmd/generate.go diff --git a/contrib/completions/bash/s2i b/contrib/completions/bash/s2i index a4e44a54f..0d41c5d0f 100644 --- a/contrib/completions/bash/s2i +++ b/contrib/completions/bash/s2i @@ -388,6 +388,41 @@ _s2i_create() noun_aliases=() } +_s2i_generate() +{ + last_command="s2i_generate" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--assemble-runtime-user=") + local_nonpersistent_flags+=("--assemble-runtime-user=") + flags+=("--assemble-user=") + local_nonpersistent_flags+=("--assemble-user=") + flags+=("--env=") + two_word_flags+=("-e") + local_nonpersistent_flags+=("--env=") + flags+=("--quiet") + flags+=("-q") + local_nonpersistent_flags+=("--quiet") + flags+=("--ca=") + flags+=("--cert=") + flags+=("--key=") + flags+=("--loglevel=") + flags+=("--tls") + flags+=("--tlsverify") + flags+=("--url=") + two_word_flags+=("-U") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _s2i_rebuild() { last_command="s2i_rebuild" @@ -519,6 +554,7 @@ _s2i() commands+=("build") commands+=("completion") commands+=("create") + commands+=("generate") commands+=("rebuild") commands+=("usage") commands+=("version") diff --git a/contrib/completions/zsh/s2i b/contrib/completions/zsh/s2i index 5d621ad9a..54fc13016 100644 --- a/contrib/completions/zsh/s2i +++ b/contrib/completions/zsh/s2i @@ -549,6 +549,41 @@ _s2i_create() noun_aliases=() } +_s2i_generate() +{ + last_command="s2i_generate" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--assemble-runtime-user=") + local_nonpersistent_flags+=("--assemble-runtime-user=") + flags+=("--assemble-user=") + local_nonpersistent_flags+=("--assemble-user=") + flags+=("--env=") + two_word_flags+=("-e") + local_nonpersistent_flags+=("--env=") + flags+=("--quiet") + flags+=("-q") + local_nonpersistent_flags+=("--quiet") + flags+=("--ca=") + flags+=("--cert=") + flags+=("--key=") + flags+=("--loglevel=") + flags+=("--tls") + flags+=("--tlsverify") + flags+=("--url=") + two_word_flags+=("-U") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _s2i_rebuild() { last_command="s2i_rebuild" @@ -680,6 +715,7 @@ _s2i() commands+=("build") commands+=("completion") commands+=("create") + commands+=("generate") commands+=("rebuild") commands+=("usage") commands+=("version") diff --git a/pkg/build/strategies/dockerfile/dockerfile.go b/pkg/build/strategies/dockerfile/dockerfile.go index b335ea4c3..7556a3576 100644 --- a/pkg/build/strategies/dockerfile/dockerfile.go +++ b/pkg/build/strategies/dockerfile/dockerfile.go @@ -168,6 +168,15 @@ func (builder *Dockerfile) CreateDockerfile(config *api.Config) error { for k, v := range config.Labels { imageLabels[k] = v } + + if len(config.ScriptsURL) > 0 { + imageLabels[constants.ScriptsURLLabel] = config.ScriptsURL + } + + if len(config.Destination) > 0 { + imageLabels[constants.DestinationLabel] = config.Destination + } + if len(imageLabels) > 0 { first := true buffer.WriteString("LABEL ") diff --git a/pkg/cmd/cli/cli.go b/pkg/cmd/cli/cli.go index 1ff9cddab..737dcea36 100644 --- a/pkg/cmd/cli/cli.go +++ b/pkg/cmd/cli/cli.go @@ -43,6 +43,7 @@ func NewCmdCLI() *cobra.Command { s2iCmd.AddCommand(cmd.NewCmdRebuild(cfg)) s2iCmd.AddCommand(cmd.NewCmdUsage(cfg)) s2iCmd.AddCommand(cmd.NewCmdCreate()) + s2iCmd.AddCommand(cmd.NewCmdGenerate(cfg)) cmdutil.SetupLogger(s2iCmd.PersistentFlags()) basename := filepath.Base(os.Args[0]) // Make case-insensitive and strip executable suffix if present diff --git a/pkg/cmd/cli/cmd/generate.go b/pkg/cmd/cli/cmd/generate.go new file mode 100644 index 000000000..3a5a27ee9 --- /dev/null +++ b/pkg/cmd/cli/cmd/generate.go @@ -0,0 +1,99 @@ +package cmd + +import ( + "context" + + "github.com/containers/image/v5/transports/alltransports" + "github.com/containers/image/v5/types" + "github.com/spf13/cobra" + + "github.com/openshift/source-to-image/pkg/api" + "github.com/openshift/source-to-image/pkg/api/constants" + "github.com/openshift/source-to-image/pkg/build/strategies/dockerfile" + "github.com/openshift/source-to-image/pkg/util/fs" +) + +// getImageLabels attempts to inspect an image existing in a remote registry. +func getImageLabels(ctx context.Context, imageName string) (map[string]string, error) { + ref, err := alltransports.ParseImageName(imageName) + if err != nil { + return nil, err + } + + img, err := ref.NewImage(ctx, &types.SystemContext{}) + if err != nil { + return nil, err + } + + imageMetadata, err := img.Inspect(ctx) + if err != nil { + return nil, err + } + + return imageMetadata.Labels, nil +} + +// generateDockerfile generates a Dockerfile with the given configuration. +func generateDockerfile(cfg *api.Config) error { + fileSystem := fs.NewFileSystem() + builder, err := dockerfile.New(cfg, fileSystem) + if err != nil { + return err + } + + _, err = builder.Build(cfg) + if err != nil { + return err + } + + return nil +} + +// adjustConfigWithImageLabels adjusts the configuration with given labels. +func adjustConfigWithImageLabels(cfg *api.Config, labels map[string]string) { + if v, ok := labels[constants.ScriptsURLLabel]; ok { + cfg.ScriptsURL = v + } + + if v, ok := labels[constants.DestinationLabel]; ok { + cfg.Destination = v + } + +} + +// NewCmdGenerate implements the S2I cli generate command. +func NewCmdGenerate(cfg *api.Config) *cobra.Command { + generateCmd := &cobra.Command{ + Use: "generate ", + Short: "Generate a Dockerfile based on the provided builder image", + Example: ` +# Generate a Dockerfile from a builder image: +$ s2i generate docker://quay.io/redhat-developer/app-binding-operator Dockerfile.gen +`, + RunE: func(cmd *cobra.Command, _ []string) error { + if cmd.Flags().NArg() != 2 { + return cmd.Help() + } + + cfg.BuilderImage = cmd.Flags().Arg(0) + cfg.AsDockerfile = cmd.Flags().Arg(1) + + ctx := context.Background() + var imageLabels map[string]string + var err error + if imageLabels, err = getImageLabels(ctx, cfg.BuilderImage); err != nil { + return err + } + + adjustConfigWithImageLabels(cfg, imageLabels) + return generateDockerfile(cfg) + }, + } + + generateCmd.Flags().BoolVarP(&(cfg.Quiet), "quiet", "q", false, "Operate quietly. Suppress all non-error output.") + generateCmd.Flags().VarP(&(cfg.Environment), "env", "e", "Specify an single environment variable in NAME=VALUE format") + generateCmd.Flags().StringVarP(&(cfg.AssembleUser), "assemble-user", "", "", "Specify the user to run assemble with") + generateCmd.Flags().StringVarP(&(cfg.AssembleRuntimeUser), "assemble-runtime-user", "", "", "Specify the user to run assemble-runtime with") + + return generateCmd +} diff --git a/test/integration/docker/integration_test.go b/test/integration/docker/integration_test.go index bba073775..a29c0c63f 100644 --- a/test/integration/docker/integration_test.go +++ b/test/integration/docker/integration_test.go @@ -151,7 +151,7 @@ func (i integrationTest) InspectImage(name string) (*dockertypes.ImageInspect, e defer cancel() resp, _, err := engineClient.ImageInspectWithRaw(ctx, name) if err != nil { - if dockerapi.IsErrImageNotFound(err) { + if dockerapi.IsErrNotFound(err) { return nil, fmt.Errorf("no such image :%q", name) } return nil, err diff --git a/test/integration/dockerfile/dockerfile_test.go b/test/integration/dockerfile/dockerfile_test.go index 16e9468d1..995101164 100644 --- a/test/integration/dockerfile/dockerfile_test.go +++ b/test/integration/dockerfile/dockerfile_test.go @@ -154,9 +154,9 @@ func TestDockerfileBuildLabels(t *testing.T) { AssembleUser: "", ImageWorkDir: "", Source: git.MustParse("https://github.com/sclorg/nodejs-ex"), - ScriptsURL: "", + ScriptsURL: "image:///scripts", Injections: api.VolumeList{}, - Destination: "", + Destination: "/destination", Environment: api.EnvironmentList{}, Labels: map[string]string{"label1": "value1", @@ -166,6 +166,8 @@ func TestDockerfileBuildLabels(t *testing.T) { AsDockerfile: filepath.Join(tempdir, "Dockerfile"), } expected := []string{ + "\"io.openshift.s2i.scripts-url\"=\"image:///scripts\"", + "\"io.openshift.s2i.destination\"=\"/destination\"", "\"io.openshift.s2i.build.commit.date\"", "\"io.openshift.s2i.build.commit.id\"", "\"io.openshift.s2i.build.commit.ref\"",