1package houdini 2 3import ( 4 "fmt" 5 "io" 6 "net/url" 7 "os" 8 "path/filepath" 9 "strings" 10 "sync" 11 "time" 12 13 "code.cloudfoundry.org/garden" 14 "github.com/charlievieth/fs" 15 "github.com/concourse/go-archive/tarfs" 16 "github.com/vito/houdini/process" 17) 18 19type UndefinedPropertyError struct { 20 Key string 21} 22 23func (err UndefinedPropertyError) Error() string { 24 return fmt.Sprintf("property does not exist: %s", err.Key) 25} 26 27type container struct { 28 spec garden.ContainerSpec 29 30 handle string 31 32 workDir string 33 hasRootfs bool 34 35 properties garden.Properties 36 propertiesL sync.RWMutex 37 38 env []string 39 40 processTracker process.ProcessTracker 41 42 graceTime time.Duration 43 graceTimeL sync.RWMutex 44} 45 46func (backend *Backend) newContainer(spec garden.ContainerSpec, id string) (*container, error) { 47 var workDir string 48 var hasRootfs bool 49 if spec.RootFSPath != "" { 50 rootfsURI, err := url.Parse(spec.RootFSPath) 51 if err != nil { 52 return nil, err 53 } 54 55 switch rootfsURI.Scheme { 56 case "raw": 57 workDir = rootfsURI.Path 58 hasRootfs = true 59 default: 60 return nil, fmt.Errorf("unsupported rootfs uri (must be raw://): %s", spec.RootFSPath) 61 } 62 } else { 63 workDir = filepath.Join(backend.containersDir, id) 64 65 err := fs.MkdirAll(workDir, 0755) 66 if err != nil { 67 return nil, err 68 } 69 } 70 71 properties := spec.Properties 72 if properties == nil { 73 properties = garden.Properties{} 74 } 75 76 return &container{ 77 spec: spec, 78 79 handle: spec.Handle, 80 81 workDir: workDir, 82 hasRootfs: hasRootfs, 83 84 properties: properties, 85 86 env: spec.Env, 87 88 processTracker: process.NewTracker(), 89 }, nil 90} 91 92func (container *container) cleanup() error { 93 if !container.hasRootfs { 94 return fs.RemoveAll(container.workDir) 95 } 96 97 return nil 98} 99 100func (container *container) Handle() string { 101 return container.handle 102} 103 104func (container *container) Stop(kill bool) error { 105 return container.processTracker.Stop(kill) 106} 107 108func (container *container) Info() (garden.ContainerInfo, error) { return garden.ContainerInfo{}, nil } 109 110func (container *container) StreamIn(spec garden.StreamInSpec) error { 111 finalDestination := filepath.Join(container.workDir, filepath.FromSlash(spec.Path)) 112 113 err := fs.MkdirAll(finalDestination, 0755) 114 if err != nil { 115 return err 116 } 117 118 err = tarfs.Extract(spec.TarStream, finalDestination) 119 if err != nil { 120 return err 121 } 122 123 return nil 124} 125 126func (container *container) StreamOut(spec garden.StreamOutSpec) (io.ReadCloser, error) { 127 if strings.HasSuffix(spec.Path, "/") { 128 spec.Path += "." 129 } 130 131 absoluteSource := container.workDir + string(os.PathSeparator) + filepath.FromSlash(spec.Path) 132 133 r, w := io.Pipe() 134 135 errs := make(chan error, 1) 136 go func() { 137 errs <- tarfs.Compress(w, filepath.Dir(absoluteSource), filepath.Base(absoluteSource)) 138 _ = w.Close() 139 }() 140 141 return waitCloser{ 142 ReadCloser: r, 143 wait: errs, 144 }, nil 145} 146 147type waitCloser struct { 148 io.ReadCloser 149 wait <-chan error 150} 151 152func (c waitCloser) Close() error { 153 err := c.ReadCloser.Close() 154 if err != nil { 155 return err 156 } 157 158 return <-c.wait 159} 160 161func (container *container) LimitBandwidth(limits garden.BandwidthLimits) error { return nil } 162 163func (container *container) CurrentBandwidthLimits() (garden.BandwidthLimits, error) { 164 return garden.BandwidthLimits{}, nil 165} 166 167func (container *container) LimitCPU(limits garden.CPULimits) error { return nil } 168 169func (container *container) CurrentCPULimits() (garden.CPULimits, error) { 170 return garden.CPULimits{}, nil 171} 172 173func (container *container) LimitDisk(limits garden.DiskLimits) error { return nil } 174 175func (container *container) CurrentDiskLimits() (garden.DiskLimits, error) { 176 return garden.DiskLimits{}, nil 177} 178 179func (container *container) LimitMemory(limits garden.MemoryLimits) error { return nil } 180 181func (container *container) CurrentMemoryLimits() (garden.MemoryLimits, error) { 182 return garden.MemoryLimits{}, nil 183} 184 185func (container *container) NetIn(hostPort, containerPort uint32) (uint32, uint32, error) { 186 return 0, 0, nil 187} 188 189func (container *container) NetOut(garden.NetOutRule) error { return nil } 190 191func (container *container) BulkNetOut([]garden.NetOutRule) error { return nil } 192 193func (container *container) Run(spec garden.ProcessSpec, processIO garden.ProcessIO) (garden.Process, error) { 194 cmd, err := container.cmd(spec) 195 if err != nil { 196 return nil, err 197 } 198 199 return container.processTracker.Run( 200 spec.ID, 201 cmd, 202 processIO, 203 spec.TTY, 204 ) 205} 206 207func (container *container) Attach(processID string, processIO garden.ProcessIO) (garden.Process, error) { 208 return container.processTracker.Attach(processID, processIO) 209} 210 211func (container *container) Property(name string) (string, error) { 212 container.propertiesL.RLock() 213 property, found := container.properties[name] 214 container.propertiesL.RUnlock() 215 216 if !found { 217 return "", UndefinedPropertyError{name} 218 } 219 220 return property, nil 221} 222 223func (container *container) SetProperty(name string, value string) error { 224 container.propertiesL.Lock() 225 container.properties[name] = value 226 container.propertiesL.Unlock() 227 228 return nil 229} 230 231func (container *container) RemoveProperty(name string) error { 232 container.propertiesL.Lock() 233 defer container.propertiesL.Unlock() 234 235 _, found := container.properties[name] 236 if !found { 237 return UndefinedPropertyError{name} 238 } 239 240 delete(container.properties, name) 241 242 return nil 243} 244 245func (container *container) Properties() (garden.Properties, error) { 246 return container.currentProperties(), nil 247} 248 249func (container *container) Metrics() (garden.Metrics, error) { 250 return garden.Metrics{}, nil 251} 252 253func (container *container) SetGraceTime(t time.Duration) error { 254 container.graceTimeL.Lock() 255 container.graceTime = t 256 container.graceTimeL.Unlock() 257 return nil 258} 259 260func (container *container) currentProperties() garden.Properties { 261 properties := garden.Properties{} 262 263 container.propertiesL.RLock() 264 265 for k, v := range container.properties { 266 properties[k] = v 267 } 268 269 container.propertiesL.RUnlock() 270 271 return properties 272} 273 274func (container *container) currentGraceTime() time.Duration { 275 container.graceTimeL.RLock() 276 defer container.graceTimeL.RUnlock() 277 return container.graceTime 278} 279