github.com/jdhenke/godel@v0.0.0-20161213181855-abeb3861bf0d/cmd/godel/update.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 godel 16 17 import ( 18 "io" 19 "io/ioutil" 20 "os" 21 "path" 22 23 "github.com/nmiyake/pkg/dirs" 24 "github.com/palantir/pkg/specdir" 25 "github.com/pkg/errors" 26 27 "github.com/palantir/godel/layout" 28 ) 29 30 // NewInstall performs a new installation of gödel in the specified directory using the specified file as the source. 31 // Calls "Install" to install the package provided as a parameter. Once the package is installed, the wrapper and 32 // settings files are copied from the newly downloaded distribution to the specified path. If there was a previous 33 // installation of gödel in the path, it is overwritten by the new file. However, changes in the "var" directory are 34 // purely additive -- files that have been added in this directory in the new distribution will be added, but existing 35 // files will not be modified or removed. 36 func NewInstall(dstDirPath, srcPkgPath string, stdout io.Writer) error { 37 if err := layout.VerifyDirExists(dstDirPath); err != nil { 38 return errors.Wrapf(err, "Path %s does not specify an existing directory", dstDirPath) 39 } 40 if err := update(dstDirPath, PkgWithChecksum{ 41 Pkg: srcPkgPath, 42 }, true, stdout); err != nil { 43 return errors.Wrapf(err, "Failed to install from %s into %s", srcPkgPath, dstDirPath) 44 } 45 return nil 46 } 47 48 // Update updates gödel. Calls "Install" to download and install the package specified in the "{{properties.Url}}" 49 // property of the properties file for the provided gödel wrapper script. Once the package is installed, the provided 50 // wrapper file and its directory are overwritten with the files provided by the package that was downloaded. However, 51 // changes in the "var" directory are purely additive -- files that have been added in this directory in the new 52 // distribution will be added, but existing files will not be modified or removed. 53 func Update(wrapperScriptPath string, stdout io.Writer) error { 54 wrapperScriptDir := path.Dir(wrapperScriptPath) 55 wrapper, err := specdir.New(wrapperScriptDir, layout.WrapperSpec(), nil, specdir.Validate) 56 if err != nil { 57 return errors.Wrapf(err, "Wrapper script %s is not in a valid location", wrapperScriptPath) 58 } 59 pkg, err := GetDistPkgInfo(wrapper.Path(layout.WrapperConfigDir)) 60 if err != nil { 61 return errors.Wrapf(err, "Failed to get URL from properties file") 62 } 63 if err := update(wrapperScriptDir, pkg, false, stdout); err != nil { 64 return errors.Wrapf(err, "Failed to update") 65 } 66 return nil 67 } 68 69 func update(wrapperScriptDir string, pkg PkgWithChecksum, newInstall bool, stdout io.Writer) error { 70 pkgSrc := pkg.ToPkgSrc() 71 72 mode := specdir.Validate 73 if newInstall { 74 mode = specdir.SpecOnly 75 } 76 wrapper, err := specdir.New(wrapperScriptDir, layout.WrapperSpec(), nil, mode) 77 if err != nil { 78 return errors.Wrapf(err, "%s is not a valid wrapper directory", wrapperScriptDir) 79 } 80 81 version, err := install(pkgSrc, stdout) 82 if err != nil { 83 return err 84 } 85 86 gödelDist, err := layout.GödelDistLayout(version, specdir.Validate) 87 if err != nil { 88 return errors.Wrapf(err, "unable to get gödel home directory") 89 } 90 91 tmpDir, cleanup, err := dirs.TempDir(wrapperScriptDir, "") 92 defer cleanup() 93 if err != nil { 94 return errors.Wrapf(err, "failed to create temporary directory in %s", wrapperScriptDir) 95 } 96 97 // copy new wrapper script to temp directory on same device and then move to destination 98 installedGödelWrapper := gödelDist.Path(layout.WrapperScriptFile) 99 tmpGödelWrapper := path.Join(tmpDir, "godelw") 100 if err := layout.CopyFile(installedGödelWrapper, tmpGödelWrapper); err != nil { 101 return errors.Wrapf(err, "failed to copy %s to %s", installedGödelWrapper, tmpGödelWrapper) 102 } 103 104 if err := os.Rename(tmpGödelWrapper, wrapper.Path(layout.WrapperScriptFile)); err != nil { 105 return errors.Wrapf(err, "failed to move wrapper script into place") 106 } 107 108 if newInstall { 109 // if this is a new install, ensure that required destination paths exist 110 if err := layout.WrapperSpec().CreateDirectoryStructure(wrapperScriptDir, nil, false); err != nil { 111 return errors.Wrapf(err, "failed to ensure that required paths exist") 112 } 113 } 114 115 // additively sync config directory 116 if err := layout.SyncDirAdditive(gödelDist.Path(layout.WrapperConfigDir), wrapper.Path(layout.WrapperConfigDir)); err != nil { 117 return errors.Wrapf(err, "failed to additively sync from %s to %s", gödelDist.Path(layout.WrapperConfigDir), wrapper.Path(layout.WrapperConfigDir)) 118 } 119 120 // overlay all directories except "config" 121 installedGödelWrapperDir := gödelDist.Path(layout.WrapperAppDir) 122 wrapperDirFiles, err := ioutil.ReadDir(installedGödelWrapperDir) 123 if err != nil { 124 return errors.Wrapf(err, "failed to list files in directory %s", installedGödelWrapperDir) 125 } 126 127 for _, currWrapperFile := range wrapperDirFiles { 128 syncSrcPath := path.Join(installedGödelWrapperDir, currWrapperFile.Name()) 129 syncDestPath := path.Join(wrapperScriptDir, layout.AppName, currWrapperFile.Name()) 130 131 if currWrapperFile.IsDir() && currWrapperFile.Name() == layout.WrapperConfigDir { 132 // do not sync "config" directory 133 continue 134 } else { 135 // if destination file exists, remove it 136 if _, err := os.Stat(syncDestPath); err == nil || !os.IsNotExist(err) { 137 if err := os.RemoveAll(syncDestPath); err != nil { 138 return errors.Wrapf(err, "failed to remove %s", syncDestPath) 139 } 140 } 141 142 // safe to copy 143 var err error 144 if currWrapperFile.IsDir() { 145 err = layout.CopyDir(syncSrcPath, syncDestPath) 146 } else { 147 err = layout.CopyFile(syncSrcPath, syncDestPath) 148 } 149 if err != nil { 150 return errors.Wrapf(err, "failed to copy %s to %s", syncSrcPath, syncDestPath) 151 } 152 } 153 } 154 155 return nil 156 }