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