github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/deployer/simple.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package deployer 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "regexp" 11 12 "github.com/juju/errors" 13 "github.com/juju/names" 14 "github.com/juju/utils/shell" 15 16 "github.com/juju/juju/agent" 17 "github.com/juju/juju/agent/tools" 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/service" 20 "github.com/juju/juju/service/common" 21 "github.com/juju/juju/version" 22 ) 23 24 // TODO(ericsnow) Use errors.Trace, etc. in this file. 25 26 // APICalls defines the interface to the API that the simple context needs. 27 type APICalls interface { 28 ConnectionInfo() (params.DeployerConnectionValues, error) 29 } 30 31 // SimpleContext is a Context that manages unit deployments on the local system. 32 type SimpleContext struct { 33 34 // api is used to get the current state server addresses at the time the 35 // given unit is deployed. 36 api APICalls 37 38 // agentConfig returns the agent config for the machine agent that is 39 // running the deployer. 40 agentConfig agent.Config 41 42 // discoverService is a surrogate for service.DiscoverService. 43 discoverService func(string, common.Conf) (deployerService, error) 44 45 // listServices is a surrogate for service.ListServices. 46 listServices func() ([]string, error) 47 } 48 49 var _ Context = (*SimpleContext)(nil) 50 51 // recursiveChmod will change the permissions on all files and 52 // folders inside path 53 func recursiveChmod(path string, mode os.FileMode) error { 54 walker := func(p string, fi os.FileInfo, err error) error { 55 if _, err := os.Stat(p); err == nil { 56 errPerm := os.Chmod(p, mode) 57 if errPerm != nil { 58 return errPerm 59 } 60 } 61 return nil 62 } 63 if err := filepath.Walk(path, walker); err != nil { 64 return err 65 } 66 return nil 67 } 68 69 // NewSimpleContext returns a new SimpleContext, acting on behalf of 70 // the specified deployer, that deploys unit agents. 71 // Paths to which agents and tools are installed are relative to dataDir. 72 func NewSimpleContext(agentConfig agent.Config, api APICalls) *SimpleContext { 73 return &SimpleContext{ 74 api: api, 75 agentConfig: agentConfig, 76 discoverService: func(name string, conf common.Conf) (deployerService, error) { 77 return service.DiscoverService(name, conf) 78 }, 79 listServices: func() ([]string, error) { 80 return service.ListServices() 81 }, 82 } 83 } 84 85 func (ctx *SimpleContext) AgentConfig() agent.Config { 86 return ctx.agentConfig 87 } 88 89 func (ctx *SimpleContext) DeployUnit(unitName, initialPassword string) (err error) { 90 // Check sanity. 91 renderer, err := shell.NewRenderer("") 92 if err != nil { 93 return errors.Trace(err) 94 } 95 svc, err := ctx.service(unitName, renderer) 96 if err != nil { 97 return errors.Trace(err) 98 } 99 installed, err := svc.Installed() 100 if err != nil { 101 return errors.Trace(err) 102 } 103 if installed { 104 return fmt.Errorf("unit %q is already deployed", unitName) 105 } 106 107 // Link the current tools for use by the new agent. 108 tag := names.NewUnitTag(unitName) 109 dataDir := ctx.agentConfig.DataDir() 110 logDir := ctx.agentConfig.LogDir() 111 _, err = tools.ChangeAgentTools(dataDir, tag.String(), version.Current) 112 toolsDir := tools.ToolsDir(dataDir, tag.String()) 113 defer removeOnErr(&err, toolsDir) 114 115 result, err := ctx.api.ConnectionInfo() 116 if err != nil { 117 return err 118 } 119 logger.Debugf("state addresses: %q", result.StateAddresses) 120 logger.Debugf("API addresses: %q", result.APIAddresses) 121 containerType := ctx.agentConfig.Value(agent.ContainerType) 122 namespace := ctx.agentConfig.Value(agent.Namespace) 123 conf, err := agent.NewAgentConfig( 124 agent.AgentConfigParams{ 125 Paths: agent.Paths{ 126 DataDir: dataDir, 127 LogDir: logDir, 128 MetricsSpoolDir: agent.DefaultPaths.MetricsSpoolDir, 129 }, 130 UpgradedToVersion: version.Current.Number, 131 Tag: tag, 132 Password: initialPassword, 133 Nonce: "unused", 134 Environment: ctx.agentConfig.Environment(), 135 // TODO: remove the state addresses here and test when api only. 136 StateAddresses: result.StateAddresses, 137 APIAddresses: result.APIAddresses, 138 CACert: ctx.agentConfig.CACert(), 139 Values: map[string]string{ 140 agent.ContainerType: containerType, 141 agent.Namespace: namespace, 142 }, 143 }) 144 if err != nil { 145 return err 146 } 147 if err := conf.Write(); err != nil { 148 return err 149 } 150 defer removeOnErr(&err, conf.Dir()) 151 152 // Install an init service that runs the unit agent. 153 if err := service.InstallAndStart(svc); err != nil { 154 return errors.Trace(err) 155 } 156 return nil 157 } 158 159 type deployerService interface { 160 Installed() (bool, error) 161 Install() error 162 Remove() error 163 Start() error 164 Stop() error 165 } 166 167 // findUpstartJob tries to find an init system job matching the 168 // given unit name in one of these formats: 169 // jujud-<deployer-tag>:<unit-tag>.conf (for compatibility) 170 // jujud-<unit-tag>.conf (default) 171 func (ctx *SimpleContext) findInitSystemJob(unitName string) (deployerService, error) { 172 unitsAndJobs, err := ctx.deployedUnitsInitSystemJobs() 173 if err != nil { 174 return nil, errors.Trace(err) 175 } 176 if job, ok := unitsAndJobs[unitName]; ok { 177 return ctx.discoverService(job, common.Conf{}) 178 } 179 return nil, errors.Errorf("unit %q is not deployed", unitName) 180 } 181 182 func (ctx *SimpleContext) RecallUnit(unitName string) error { 183 svc, err := ctx.findInitSystemJob(unitName) 184 if err != nil { 185 return errors.Trace(err) 186 } 187 installed, err := svc.Installed() 188 if err != nil { 189 return errors.Trace(err) 190 } 191 if !installed { 192 return errors.Errorf("unit %q is not deployed", unitName) 193 } 194 if err := svc.Stop(); err != nil { 195 return err 196 } 197 if err := svc.Remove(); err != nil { 198 return err 199 } 200 tag := names.NewUnitTag(unitName) 201 dataDir := ctx.agentConfig.DataDir() 202 agentDir := agent.Dir(dataDir, tag) 203 // Recursivley change mode to 777 on windows to avoid 204 // Operation not permitted errors when deleting the agentDir 205 err = recursiveChmod(agentDir, os.FileMode(0777)) 206 if err != nil { 207 return err 208 } 209 if err := os.RemoveAll(agentDir); err != nil { 210 return err 211 } 212 // TODO(dfc) should take a Tag 213 toolsDir := tools.ToolsDir(dataDir, tag.String()) 214 return os.Remove(toolsDir) 215 } 216 217 var deployedRe = regexp.MustCompile("^(jujud-.*unit-([a-z0-9-]+)-([0-9]+))$") 218 219 func (ctx *SimpleContext) deployedUnitsInitSystemJobs() (map[string]string, error) { 220 fis, err := ctx.listServices() 221 if err != nil { 222 return nil, err 223 } 224 if err != nil { 225 return nil, err 226 } 227 installed := make(map[string]string) 228 for _, fi := range fis { 229 if groups := deployedRe.FindStringSubmatch(fi); len(groups) > 0 { 230 unitName := groups[2] + "/" + groups[3] 231 if !names.IsValidUnit(unitName) { 232 continue 233 } 234 installed[unitName] = groups[1] 235 } 236 } 237 return installed, nil 238 } 239 240 func (ctx *SimpleContext) DeployedUnits() ([]string, error) { 241 unitsAndJobs, err := ctx.deployedUnitsInitSystemJobs() 242 if err != nil { 243 return nil, err 244 } 245 var installed []string 246 for unitName := range unitsAndJobs { 247 installed = append(installed, unitName) 248 } 249 return installed, nil 250 } 251 252 // service returns a service.Service corresponding to the specified 253 // unit. 254 func (ctx *SimpleContext) service(unitName string, renderer shell.Renderer) (deployerService, error) { 255 tag := names.NewUnitTag(unitName).String() 256 svcName := "jujud-" + tag 257 258 info := service.NewAgentInfo( 259 service.AgentKindUnit, 260 unitName, 261 ctx.agentConfig.DataDir(), 262 ctx.agentConfig.LogDir(), 263 ) 264 265 // TODO(thumper): 2013-09-02 bug 1219630 266 // As much as I'd like to remove JujuContainerType now, it is still 267 // needed as MAAS still needs it at this stage, and we can't fix 268 // everything at once. 269 containerType := ctx.agentConfig.Value(agent.ContainerType) 270 271 conf := service.ContainerAgentConf(info, renderer, containerType) 272 return ctx.discoverService(svcName, conf) 273 } 274 275 func removeOnErr(err *error, path string) { 276 if *err != nil { 277 if err := os.RemoveAll(path); err != nil { 278 logger.Warningf("installer: cannot remove %q: %v", path, err) 279 } 280 } 281 }