github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/worker/container.go (about) 1 package worker 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "io" 10 11 "code.cloudfoundry.org/garden" 12 "code.cloudfoundry.org/lager" 13 "github.com/pf-qiu/concourse/v6/atc/db" 14 "github.com/pf-qiu/concourse/v6/atc/runtime" 15 "github.com/pf-qiu/concourse/v6/atc/worker/gclient" 16 ) 17 18 var ErrMissingVolume = errors.New("volume mounted to container is missing") 19 20 //go:generate counterfeiter . Container 21 22 type Container interface { 23 gclient.Container 24 runtime.Runner 25 26 // TODO: get rid of this, its not used anywhere 27 Destroy() error 28 29 VolumeMounts() []VolumeMount 30 31 // TODO: get rid of this, its not used anywhere 32 WorkerName() string 33 34 UpdateLastHijack() error 35 } 36 37 type gardenWorkerContainer struct { 38 gclient.Container 39 dbContainer db.CreatedContainer 40 dbVolumes []db.CreatedVolume 41 42 gardenClient gclient.Client 43 44 volumeMounts []VolumeMount 45 46 user string 47 workerName string 48 } 49 50 func newGardenWorkerContainer( 51 logger lager.Logger, 52 container gclient.Container, 53 dbContainer db.CreatedContainer, 54 dbContainerVolumes []db.CreatedVolume, 55 gardenClient gclient.Client, 56 volumeClient VolumeClient, 57 workerName string, 58 ) (Container, error) { 59 logger = logger.WithData( 60 lager.Data{ 61 "container": container.Handle(), 62 "worker": workerName, 63 }, 64 ) 65 66 workerContainer := &gardenWorkerContainer{ 67 Container: container, 68 dbContainer: dbContainer, 69 dbVolumes: dbContainerVolumes, 70 71 gardenClient: gardenClient, 72 73 workerName: workerName, 74 } 75 76 err := workerContainer.initializeVolumes(logger, volumeClient) 77 if err != nil { 78 return nil, err 79 } 80 81 properties, err := workerContainer.Properties() 82 if err != nil { 83 return nil, err 84 } 85 86 if properties["user"] != "" { 87 workerContainer.user = properties["user"] 88 } else { 89 workerContainer.user = "root" 90 } 91 92 return workerContainer, nil 93 } 94 95 func (container *gardenWorkerContainer) Destroy() error { 96 return container.gardenClient.Destroy(container.Handle()) 97 } 98 99 func (container *gardenWorkerContainer) WorkerName() string { 100 return container.workerName 101 } 102 103 func (container *gardenWorkerContainer) UpdateLastHijack() error { 104 return container.dbContainer.UpdateLastHijack() 105 } 106 107 func (container *gardenWorkerContainer) Run(ctx context.Context, spec garden.ProcessSpec, io garden.ProcessIO) (garden.Process, error) { 108 spec.User = container.user 109 return container.Container.Run(ctx, spec, io) 110 } 111 112 func (container *gardenWorkerContainer) VolumeMounts() []VolumeMount { 113 return container.volumeMounts 114 } 115 116 func (container *gardenWorkerContainer) initializeVolumes( 117 logger lager.Logger, 118 volumeClient VolumeClient, 119 ) error { 120 121 volumeMounts := []VolumeMount{} 122 123 for _, dbVolume := range container.dbVolumes { 124 volumeLogger := logger.Session("volume", lager.Data{ 125 "handle": dbVolume.Handle(), 126 }) 127 128 volume, volumeFound, err := volumeClient.LookupVolume(logger, dbVolume.Handle()) 129 if err != nil { 130 volumeLogger.Error("failed-to-lookup-volume", err) 131 return err 132 } 133 134 if !volumeFound { 135 volumeLogger.Error("volume-is-missing-on-worker", ErrMissingVolume, lager.Data{"handle": dbVolume.Handle()}) 136 return errors.New("volume mounted to container is missing " + dbVolume.Handle() + " from worker " + container.workerName) 137 } 138 139 volumeMounts = append(volumeMounts, VolumeMount{ 140 Volume: volume, 141 MountPath: dbVolume.Path(), 142 }) 143 } 144 145 container.volumeMounts = volumeMounts 146 147 return nil 148 } 149 150 // TODO (runtime/#4910): this needs to be modified to not be resource specific 151 // the stdout of the run() is expected to be of json format 152 // this will break if used with task_step as it does not 153 // print out json 154 func (container *gardenWorkerContainer) RunScript( 155 ctx context.Context, 156 path string, 157 args []string, 158 input []byte, 159 output interface{}, 160 logDest io.Writer, 161 recoverable bool, 162 ) error { 163 if recoverable { 164 result, _ := container.Properties() 165 code := result[runtime.ResourceResultPropertyName] 166 if code != "" { 167 return json.Unmarshal([]byte(code), &output) 168 } 169 } 170 171 stdout := new(bytes.Buffer) 172 stderr := new(bytes.Buffer) 173 174 processIO := garden.ProcessIO{ 175 Stdin: bytes.NewBuffer(input), 176 Stdout: stdout, 177 } 178 179 if logDest != nil { 180 processIO.Stderr = logDest 181 } else { 182 processIO.Stderr = stderr 183 } 184 185 var process garden.Process 186 var err error 187 if recoverable { 188 process, err = container.Attach(ctx, runtime.ResourceProcessID, processIO) 189 if err != nil { 190 process, err = container.Run( 191 ctx, 192 garden.ProcessSpec{ 193 ID: runtime.ResourceProcessID, 194 Path: path, 195 Args: args, 196 }, processIO) 197 if err != nil { 198 return err 199 } 200 } 201 } else { 202 process, err = container.Run(ctx, garden.ProcessSpec{ 203 Path: path, 204 Args: args, 205 }, processIO) 206 if err != nil { 207 return err 208 } 209 } 210 211 processExited := make(chan struct{}) 212 213 var processStatus int 214 var processErr error 215 216 go func() { 217 processStatus, processErr = process.Wait() 218 close(processExited) 219 }() 220 221 select { 222 case <-processExited: 223 if processErr != nil { 224 return processErr 225 } 226 227 if processStatus != 0 { 228 return runtime.ErrResourceScriptFailed{ 229 Path: path, 230 Args: args, 231 ExitStatus: processStatus, 232 233 Stderr: stderr.String(), 234 } 235 } 236 237 if recoverable { 238 err := container.SetProperty(runtime.ResourceResultPropertyName, stdout.String()) 239 if err != nil { 240 return err 241 } 242 } 243 244 err := json.Unmarshal(stdout.Bytes(), output) 245 if err != nil { 246 return fmt.Errorf("%s\n\nwhen parsing resource response:\n\n%s", err, stdout.String()) 247 } 248 return err 249 250 case <-ctx.Done(): 251 _ = container.Stop(false) 252 <-processExited 253 return ctx.Err() 254 } 255 }