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