github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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 // TODO(dfc) 112 _, err = tools.ChangeAgentTools(dataDir, tag.String(), version.Current) 113 // TODO(dfc) 114 toolsDir := tools.ToolsDir(dataDir, tag.String()) 115 defer removeOnErr(&err, toolsDir) 116 117 result, err := ctx.api.ConnectionInfo() 118 if err != nil { 119 return err 120 } 121 logger.Debugf("state addresses: %q", result.StateAddresses) 122 logger.Debugf("API addresses: %q", result.APIAddresses) 123 containerType := ctx.agentConfig.Value(agent.ContainerType) 124 namespace := ctx.agentConfig.Value(agent.Namespace) 125 conf, err := agent.NewAgentConfig( 126 agent.AgentConfigParams{ 127 DataDir: dataDir, 128 LogDir: logDir, 129 UpgradedToVersion: version.Current.Number, 130 Tag: tag, 131 Password: initialPassword, 132 Nonce: "unused", 133 Environment: ctx.agentConfig.Environment(), 134 // TODO: remove the state addresses here and test when api only. 135 StateAddresses: result.StateAddresses, 136 APIAddresses: result.APIAddresses, 137 CACert: ctx.agentConfig.CACert(), 138 Values: map[string]string{ 139 agent.ContainerType: containerType, 140 agent.Namespace: namespace, 141 }, 142 }) 143 if err != nil { 144 return err 145 } 146 if err := conf.Write(); err != nil { 147 return err 148 } 149 defer removeOnErr(&err, conf.Dir()) 150 151 // Install an init service that runs the unit agent. 152 if err := service.InstallAndStart(svc); err != nil { 153 return errors.Trace(err) 154 } 155 return nil 156 } 157 158 type deployerService interface { 159 Installed() (bool, error) 160 Install() error 161 Remove() error 162 Start() error 163 Stop() error 164 } 165 166 // findUpstartJob tries to find an init system job matching the 167 // given unit name in one of these formats: 168 // jujud-<deployer-tag>:<unit-tag>.conf (for compatibility) 169 // jujud-<unit-tag>.conf (default) 170 func (ctx *SimpleContext) findInitSystemJob(unitName string) (deployerService, error) { 171 unitsAndJobs, err := ctx.deployedUnitsInitSystemJobs() 172 if err != nil { 173 return nil, errors.Trace(err) 174 } 175 if job, ok := unitsAndJobs[unitName]; ok { 176 return ctx.discoverService(job, common.Conf{}) 177 } 178 return nil, errors.Errorf("unit %q is not deployed", unitName) 179 } 180 181 func (ctx *SimpleContext) RecallUnit(unitName string) error { 182 svc, err := ctx.findInitSystemJob(unitName) 183 if err != nil { 184 return errors.Trace(err) 185 } 186 installed, err := svc.Installed() 187 if err != nil { 188 return errors.Trace(err) 189 } 190 if !installed { 191 return errors.Errorf("unit %q is not deployed", unitName) 192 } 193 if err := svc.Stop(); err != nil { 194 return err 195 } 196 if err := svc.Remove(); err != nil { 197 return err 198 } 199 tag := names.NewUnitTag(unitName) 200 dataDir := ctx.agentConfig.DataDir() 201 agentDir := agent.Dir(dataDir, tag) 202 // Recursivley change mode to 777 on windows to avoid 203 // Operation not permitted errors when deleting the agentDir 204 err = recursiveChmod(agentDir, os.FileMode(0777)) 205 if err != nil { 206 return err 207 } 208 if err := os.RemoveAll(agentDir); err != nil { 209 return err 210 } 211 // TODO(dfc) should take a Tag 212 toolsDir := tools.ToolsDir(dataDir, tag.String()) 213 return os.Remove(toolsDir) 214 } 215 216 var deployedRe = regexp.MustCompile("^(jujud-.*unit-([a-z0-9-]+)-([0-9]+))$") 217 218 func (ctx *SimpleContext) deployedUnitsInitSystemJobs() (map[string]string, error) { 219 fis, err := ctx.listServices() 220 if err != nil { 221 return nil, err 222 } 223 if err != nil { 224 return nil, err 225 } 226 installed := make(map[string]string) 227 for _, fi := range fis { 228 if groups := deployedRe.FindStringSubmatch(fi); len(groups) > 0 { 229 unitName := groups[2] + "/" + groups[3] 230 if !names.IsValidUnit(unitName) { 231 continue 232 } 233 installed[unitName] = groups[1] 234 } 235 } 236 return installed, nil 237 } 238 239 func (ctx *SimpleContext) DeployedUnits() ([]string, error) { 240 unitsAndJobs, err := ctx.deployedUnitsInitSystemJobs() 241 if err != nil { 242 return nil, err 243 } 244 var installed []string 245 for unitName := range unitsAndJobs { 246 installed = append(installed, unitName) 247 } 248 return installed, nil 249 } 250 251 // service returns a service.Service corresponding to the specified 252 // unit. 253 func (ctx *SimpleContext) service(unitName string, renderer shell.Renderer) (deployerService, error) { 254 tag := names.NewUnitTag(unitName).String() 255 svcName := "jujud-" + tag 256 257 info := service.NewAgentInfo( 258 service.AgentKindUnit, 259 unitName, 260 ctx.agentConfig.DataDir(), 261 ctx.agentConfig.LogDir(), 262 ) 263 264 // TODO(thumper): 2013-09-02 bug 1219630 265 // As much as I'd like to remove JujuContainerType now, it is still 266 // needed as MAAS still needs it at this stage, and we can't fix 267 // everything at once. 268 containerType := ctx.agentConfig.Value(agent.ContainerType) 269 270 conf := service.ContainerAgentConf(info, renderer, containerType) 271 return ctx.discoverService(svcName, conf) 272 } 273 274 func removeOnErr(err *error, path string) { 275 if *err != nil { 276 if err := os.RemoveAll(path); err != nil { 277 logger.Warningf("installer: cannot remove %q: %v", path, err) 278 } 279 } 280 }