github.com/fabianvf/ocp-release-operator-sdk@v0.0.0-20190426141702-57620ee2f090/pkg/ansible/runner/internal/inputdir/inputdir.go (about) 1 // Copyright 2018 The Operator-SDK Authors 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 inputdir 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "os" 22 "path/filepath" 23 "strings" 24 25 "github.com/spf13/afero" 26 27 "github.com/operator-framework/operator-sdk/internal/util/fileutil" 28 29 logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" 30 ) 31 32 var log = logf.Log.WithName("inputdir") 33 34 // InputDir represents an input directory for ansible-runner. 35 type InputDir struct { 36 Path string 37 PlaybookPath string 38 Parameters map[string]interface{} 39 EnvVars map[string]string 40 Settings map[string]string 41 } 42 43 // makeDirs creates the required directory structure. 44 func (i *InputDir) makeDirs() error { 45 for _, path := range []string{"env", "project", "inventory"} { 46 fullPath := filepath.Join(i.Path, path) 47 err := os.MkdirAll(fullPath, os.ModePerm) 48 if err != nil { 49 log.Error(err, "Unable to create directory", "Path", fullPath) 50 return err 51 } 52 } 53 return nil 54 } 55 56 // addFile adds a file to the given relative path within the input directory. 57 func (i *InputDir) addFile(path string, content []byte) error { 58 fullPath := filepath.Join(i.Path, path) 59 err := ioutil.WriteFile(fullPath, content, 0644) 60 if err != nil { 61 log.Error(err, "Unable to write file", "Path", fullPath) 62 } 63 return err 64 } 65 66 // copyInventory copies a file or directory from src to dst 67 func (i *InputDir) copyInventory(src string, dst string) error { 68 fs := afero.NewOsFs() 69 return afero.Walk(fs, src, 70 func(path string, info os.FileInfo, err error) error { 71 if err != nil { 72 return err 73 } 74 fullDst := strings.Replace(path, src, dst, 1) 75 if info.IsDir() { 76 if err = fs.MkdirAll(fullDst, info.Mode()); err != nil { 77 return err 78 } 79 } else { 80 f, err := fs.Open(path) 81 if err != nil { 82 return err 83 } 84 if err = afero.WriteReader(fs, fullDst, f); err != nil { 85 return err 86 } 87 if err = fs.Chmod(fullDst, info.Mode()); err != nil { 88 return err 89 } 90 } 91 return nil 92 }) 93 } 94 95 // Stdout reads the stdout from the ansible artifact that corresponds to the 96 // given ident and returns it as a string. 97 func (i *InputDir) Stdout(ident string) (string, error) { 98 errorPath := filepath.Join(i.Path, "artifacts", ident, "stdout") 99 errorText, err := ioutil.ReadFile(errorPath) 100 return string(errorText), err 101 } 102 103 // Write commits the object's state to the filesystem at i.Path. 104 func (i *InputDir) Write() error { 105 paramBytes, err := json.Marshal(i.Parameters) 106 if err != nil { 107 return err 108 } 109 envVarBytes, err := json.Marshal(i.EnvVars) 110 if err != nil { 111 return err 112 } 113 settingsBytes, err := json.Marshal(i.Settings) 114 if err != nil { 115 return err 116 } 117 118 err = i.makeDirs() 119 if err != nil { 120 return err 121 } 122 123 err = i.addFile("env/envvars", envVarBytes) 124 if err != nil { 125 return err 126 } 127 err = i.addFile("env/extravars", paramBytes) 128 if err != nil { 129 return err 130 } 131 err = i.addFile("env/settings", settingsBytes) 132 if err != nil { 133 return err 134 } 135 136 // ANSIBLE_INVENTORY takes precedence over our generated hosts file 137 // so if the envvar is set we don't bother making it, we just copy 138 // the inventory into our runner directory 139 ansible_inventory := os.Getenv("ANSIBLE_INVENTORY") 140 if ansible_inventory == "" { 141 // If ansible-runner is running in a python virtual environment, propagate 142 // that to ansible. 143 venv := os.Getenv("VIRTUAL_ENV") 144 hosts := "localhost ansible_connection=local" 145 if venv != "" { 146 hosts = fmt.Sprintf("%s ansible_python_interpreter=%s", hosts, filepath.Join(venv, "bin/python")) 147 } 148 err = i.addFile("inventory/hosts", []byte(hosts)) 149 if err != nil { 150 return err 151 } 152 } else { 153 fi, err := os.Stat(ansible_inventory) 154 if err != nil { 155 return err 156 } 157 switch mode := fi.Mode(); { 158 case mode.IsDir(): 159 err = i.copyInventory(ansible_inventory, filepath.Join(i.Path, "inventory")) 160 if err != nil { 161 return err 162 } 163 case mode.IsRegular(): 164 err = i.copyInventory(ansible_inventory, filepath.Join(i.Path, "inventory/hosts")) 165 if err != nil { 166 return err 167 } 168 } 169 } 170 171 if i.PlaybookPath != "" { 172 f, err := os.Open(i.PlaybookPath) 173 if err != nil { 174 log.Error(err, "Failed to open playbook file", "Path", i.PlaybookPath) 175 return err 176 } 177 defer func() { 178 if err := f.Close(); err != nil && !fileutil.IsClosedError(err) { 179 log.Error(err, "Failed to close playbook file") 180 } 181 }() 182 183 playbookBytes, err := ioutil.ReadAll(f) 184 if err != nil { 185 return err 186 } 187 188 err = i.addFile("project/playbook.yaml", playbookBytes) 189 if err != nil { 190 return err 191 } 192 } 193 return nil 194 }