github.com/nak3/source-to-image@v1.1.10-0.20180319140719-2ed55639898d/pkg/build/strategies/onbuild/onbuild.go (about) 1 package onbuild 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 10 "github.com/openshift/source-to-image/pkg/api" 11 "github.com/openshift/source-to-image/pkg/build" 12 "github.com/openshift/source-to-image/pkg/build/strategies/sti" 13 "github.com/openshift/source-to-image/pkg/docker" 14 "github.com/openshift/source-to-image/pkg/ignore" 15 "github.com/openshift/source-to-image/pkg/scm" 16 "github.com/openshift/source-to-image/pkg/scm/git" 17 "github.com/openshift/source-to-image/pkg/scripts" 18 "github.com/openshift/source-to-image/pkg/tar" 19 "github.com/openshift/source-to-image/pkg/util/cmd" 20 "github.com/openshift/source-to-image/pkg/util/fs" 21 utilstatus "github.com/openshift/source-to-image/pkg/util/status" 22 ) 23 24 // OnBuild strategy executes the simple Docker build in case the image does not 25 // support STI scripts but has ONBUILD instructions recorded. 26 type OnBuild struct { 27 docker docker.Docker 28 git git.Git 29 fs fs.FileSystem 30 tar tar.Tar 31 source build.SourceHandler 32 garbage build.Cleaner 33 } 34 35 type onBuildSourceHandler struct { 36 build.Downloader 37 build.Preparer 38 build.Ignorer 39 } 40 41 // New returns a new instance of OnBuild builder 42 func New(client docker.Client, config *api.Config, fs fs.FileSystem, overrides build.Overrides) (*OnBuild, error) { 43 dockerHandler := docker.New(client, config.PullAuthentication) 44 builder := &OnBuild{ 45 docker: dockerHandler, 46 git: git.New(fs, cmd.NewCommandRunner()), 47 fs: fs, 48 tar: tar.New(fs), 49 } 50 // Use STI Prepare() and download the 'run' script optionally. 51 s, err := sti.New(client, config, fs, overrides) 52 if err != nil { 53 return nil, err 54 } 55 s.SetScripts([]string{}, []string{api.Assemble, api.Run}) 56 57 downloader := overrides.Downloader 58 if downloader == nil { 59 downloader, err = scm.DownloaderForSource(builder.fs, config.Source, config.ForceCopy) 60 if err != nil { 61 return nil, err 62 } 63 } 64 65 builder.source = onBuildSourceHandler{ 66 Downloader: downloader, 67 Preparer: s, 68 Ignorer: &ignore.DockerIgnorer{}, 69 } 70 71 builder.garbage = build.NewDefaultCleaner(builder.fs, builder.docker) 72 return builder, nil 73 } 74 75 // Build executes the ONBUILD kind of build 76 func (builder *OnBuild) Build(config *api.Config) (*api.Result, error) { 77 buildResult := &api.Result{} 78 79 if config.BlockOnBuild { 80 buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( 81 utilstatus.ReasonOnBuildForbidden, 82 utilstatus.ReasonMessageOnBuildForbidden, 83 ) 84 return buildResult, fmt.Errorf("builder image uses ONBUILD instructions but ONBUILD is not allowed") 85 } 86 glog.V(2).Info("Preparing the source code for build") 87 // Change the installation directory for this config to store scripts inside 88 // the application root directory. 89 if err := builder.source.Prepare(config); err != nil { 90 return buildResult, err 91 } 92 93 // If necessary, copy the STI scripts into application root directory 94 builder.copySTIScripts(config) 95 96 glog.V(2).Info("Creating application Dockerfile") 97 if err := builder.CreateDockerfile(config); err != nil { 98 buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( 99 utilstatus.ReasonDockerfileCreateFailed, 100 utilstatus.ReasonMessageDockerfileCreateFailed, 101 ) 102 return buildResult, err 103 } 104 105 glog.V(2).Info("Creating application source code image") 106 tarStream := builder.tar.CreateTarStreamReader(filepath.Join(config.WorkingDir, "upload", "src"), false) 107 defer tarStream.Close() 108 109 outReader, outWriter := io.Pipe() 110 go io.Copy(os.Stdout, outReader) 111 112 opts := docker.BuildImageOptions{ 113 Name: config.Tag, 114 Stdin: tarStream, 115 Stdout: outWriter, 116 CGroupLimits: config.CGroupLimits, 117 } 118 119 glog.V(2).Info("Building the application source") 120 if err := builder.docker.BuildImage(opts); err != nil { 121 buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( 122 utilstatus.ReasonDockerImageBuildFailed, 123 utilstatus.ReasonMessageDockerImageBuildFailed, 124 ) 125 return buildResult, err 126 } 127 128 glog.V(2).Info("Cleaning up temporary containers") 129 builder.garbage.Cleanup(config) 130 131 var imageID string 132 var err error 133 if len(opts.Name) > 0 { 134 if imageID, err = builder.docker.GetImageID(opts.Name); err != nil { 135 buildResult.BuildInfo.FailureReason = utilstatus.NewFailureReason( 136 utilstatus.ReasonGenericS2IBuildFailed, 137 utilstatus.ReasonMessageGenericS2iBuildFailed, 138 ) 139 return buildResult, err 140 } 141 } 142 143 return &api.Result{ 144 Success: true, 145 WorkingDir: config.WorkingDir, 146 ImageID: imageID, 147 }, nil 148 } 149 150 // CreateDockerfile creates the ONBUILD Dockerfile 151 func (builder *OnBuild) CreateDockerfile(config *api.Config) error { 152 buffer := bytes.Buffer{} 153 uploadDir := filepath.Join(config.WorkingDir, "upload", "src") 154 buffer.WriteString(fmt.Sprintf("FROM %s\n", config.BuilderImage)) 155 entrypoint, err := GuessEntrypoint(builder.fs, uploadDir) 156 if err != nil { 157 return err 158 } 159 env, err := scripts.GetEnvironment(config) 160 if err != nil { 161 glog.V(1).Infof("Environment: %v", err) 162 } else { 163 buffer.WriteString(scripts.ConvertEnvironmentToDocker(env)) 164 } 165 // If there is an assemble script present, run it as part of the build process 166 // as the last thing. 167 if builder.hasAssembleScript(config) { 168 buffer.WriteString("RUN sh assemble\n") 169 } 170 // FIXME: This assumes that the WORKDIR is set to the application source root 171 // directory. 172 buffer.WriteString(fmt.Sprintf(`ENTRYPOINT ["./%s"]`+"\n", entrypoint)) 173 return builder.fs.WriteFile(filepath.Join(uploadDir, "Dockerfile"), buffer.Bytes()) 174 } 175 176 func (builder *OnBuild) copySTIScripts(config *api.Config) { 177 scriptsPath := filepath.Join(config.WorkingDir, "upload", "scripts") 178 sourcePath := filepath.Join(config.WorkingDir, "upload", "src") 179 if _, err := builder.fs.Stat(filepath.Join(scriptsPath, api.Run)); err == nil { 180 glog.V(3).Info("Found S2I 'run' script, copying to application source dir") 181 builder.fs.Copy(filepath.Join(scriptsPath, api.Run), filepath.Join(sourcePath, api.Run)) 182 } 183 if _, err := builder.fs.Stat(filepath.Join(scriptsPath, api.Assemble)); err == nil { 184 glog.V(3).Info("Found S2I 'assemble' script, copying to application source dir") 185 builder.fs.Copy(filepath.Join(scriptsPath, api.Assemble), filepath.Join(sourcePath, api.Assemble)) 186 } 187 } 188 189 // hasAssembleScript checks if the the assemble script is available 190 func (builder *OnBuild) hasAssembleScript(config *api.Config) bool { 191 assemblePath := filepath.Join(config.WorkingDir, "upload", "src", api.Assemble) 192 _, err := builder.fs.Stat(assemblePath) 193 return err == nil 194 }