github.com/jonsyu1/godel@v0.0.0-20171017211503-64567a0cf169/apps/distgo/cmd/docker/build.go (about) 1 // Copyright 2016 Palantir Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package docker 16 17 import ( 18 "fmt" 19 "io" 20 "io/ioutil" 21 "os" 22 "path" 23 24 "github.com/pkg/errors" 25 26 "github.com/palantir/godel/apps/distgo/cmd/build" 27 "github.com/palantir/godel/apps/distgo/cmd/dist" 28 "github.com/palantir/godel/apps/distgo/params" 29 ) 30 31 func Build(products []string, cfg params.Project, wd, baseRepo string, verbose bool, stdout io.Writer) error { 32 // the docker build tasks first runs dist task on the products 33 // on which the docker images have a dependency. after building the dists, 34 // the images are built in ordered way since the images can have dependencies among themselves. 35 productsToDist, productsToBuildImage, err := productsToDistAndBuildImage(products, cfg) 36 if err != nil { 37 return err 38 } 39 40 productsToDistRequired, err := dist.RequiresDist(productsToDist, cfg, wd) 41 if err != nil { 42 return err 43 } 44 if len(productsToDistRequired) != 0 { 45 // run the dist task 46 if err := dist.Products(productsToDistRequired, cfg, false, wd, stdout); err != nil { 47 return err 48 } 49 } 50 51 // build docker images 52 buildSpecsWithDeps, err := build.SpecsWithDepsForArgs(cfg, productsToBuildImage, wd) 53 if err != nil { 54 return err 55 } 56 orderedSpecs, err := OrderBuildSpecs(buildSpecsWithDeps) 57 if err != nil { 58 return err 59 } 60 if baseRepo != "" { 61 // if base repo is specified, join it to each image's repo 62 for i := range orderedSpecs { 63 for j := range orderedSpecs[i].Spec.DockerImages { 64 orderedSpecs[i].Spec.DockerImages[j].Repository = path.Join(baseRepo, 65 orderedSpecs[i].Spec.DockerImages[j].Repository) 66 } 67 } 68 } 69 return RunBuild(orderedSpecs, verbose, stdout) 70 } 71 72 func RunBuild(buildSpecsWithDeps []params.ProductBuildSpecWithDeps, verbose bool, stdout io.Writer) error { 73 specsMap := buildSpecsMap(buildSpecsWithDeps) 74 for i := range buildSpecsWithDeps { 75 for _, image := range buildSpecsWithDeps[i].Spec.DockerImages { 76 if err := buildImage(image, buildSpecsWithDeps[i], specsMap, verbose, stdout); err != nil { 77 return err 78 } 79 } 80 } 81 return nil 82 } 83 84 func buildImage(image params.DockerImage, buildSpecsWithDeps params.ProductBuildSpecWithDeps, specsMap map[string]params.ProductBuildSpecWithDeps, verbose bool, stdout io.Writer) error { 85 fmt.Fprintf(stdout, "Building docker image for %s and tagging it as %s:%s\n", buildSpecsWithDeps.Spec.ProductName, image.Repository, image.Tag) 86 87 contextDir := path.Join(buildSpecsWithDeps.Spec.ProjectDir, image.ContextDir) 88 89 // link dependent dist artifacts into the context directory 90 for depProduct, depTypes := range dockerDepsToMap(image.Deps) { 91 for depType, targetFile := range depTypes { 92 if !isDist(depType) { 93 continue 94 } 95 if _, ok := specsMap[depProduct]; !ok { 96 return errors.Errorf("Unable to find the dependent product %v for %v", 97 depProduct, buildSpecsWithDeps.Spec.ProductName) 98 } 99 depSpec := specsMap[depProduct].Spec 100 distsMap := buildDistsMap(specsMap[depProduct].Spec) 101 if _, ok := distsMap[string(depType)]; !ok { 102 return errors.Errorf("Unable to find dist type %v on the dependent product %v for %v", 103 depType, depProduct, buildSpecsWithDeps.Spec.ProductName) 104 } 105 distCfg := distsMap[string(depType)] 106 107 for _, artifactLocation := range dist.FullArtifactsPaths(dist.ToDister(distCfg.Info), depSpec, distCfg) { 108 if targetFile == "" { 109 targetFile = path.Base(artifactLocation) 110 } 111 target := path.Join(contextDir, targetFile) 112 if _, err := os.Stat(target); err == nil { 113 // ensure the target does not exists before creating a new one 114 if err := os.Remove(target); err != nil { 115 return err 116 } 117 } 118 if err := os.Link(artifactLocation, target); err != nil { 119 return err 120 } 121 } 122 } 123 } 124 builder := GetBuilder(image) 125 buildWriter := ioutil.Discard 126 if verbose { 127 buildWriter = stdout 128 } 129 return builder.build(buildSpecsWithDeps, buildWriter) 130 } 131 132 func dockerDepsToMap(deps []params.DockerDep) map[string]map[params.DockerDepType]string { 133 m := make(map[string]map[params.DockerDepType]string) 134 for _, dep := range deps { 135 if m[dep.Product] == nil { 136 m[dep.Product] = make(map[params.DockerDepType]string) 137 } 138 m[dep.Product][dep.Type] = dep.TargetFile 139 } 140 return m 141 } 142 143 func buildSpecsMap(specs []params.ProductBuildSpecWithDeps) map[string]params.ProductBuildSpecWithDeps { 144 specMap := make(map[string]params.ProductBuildSpecWithDeps) 145 for _, spec := range specs { 146 specMap[spec.Spec.ProductName] = spec 147 } 148 return specMap 149 } 150 151 func buildDistsMap(spec params.ProductBuildSpec) map[string]params.Dist { 152 distMap := make(map[string]params.Dist) 153 for _, dist := range spec.Dist { 154 distMap[string(dist.Info.Type())] = dist 155 } 156 return distMap 157 }