github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cloudconfig/userdatacfg.go (about) 1 // Copyright 2012, 2013, 2014, 2015 Canonical Ltd. 2 // Copyright 2014, 2015 Cloudbase Solutions SRL 3 // Licensed under the AGPLv3, see LICENCE file for details. 4 5 package cloudconfig 6 7 import ( 8 "fmt" 9 10 "github.com/juju/errors" 11 "github.com/juju/names" 12 "github.com/juju/utils" 13 14 "github.com/juju/juju/agent" 15 "github.com/juju/juju/cloudconfig/cloudinit" 16 "github.com/juju/juju/cloudconfig/instancecfg" 17 "github.com/juju/juju/version" 18 ) 19 20 const ( 21 // fileSchemePrefix is the prefix for file:// URLs. 22 fileSchemePrefix = "file://" 23 24 // NonceFile is written by cloud-init as the last thing it does. 25 // The file will contain the machine's nonce. The filename is 26 // relative to the Juju data-dir. 27 NonceFile = "nonce.txt" 28 ) 29 30 // UserdataConfig is the bridge between instancecfg and cloudinit 31 // It supports different levels of configuration for instances 32 type UserdataConfig interface { 33 // Configure is a convenience function that updates the cloudinit.Config 34 // with appropriate configuration. It will run ConfigureBasic() and 35 // ConfigureJuju() 36 Configure() error 37 // ConfigureBasic updates the provided cloudinit.Config with 38 // basic configuration to initialise an OS image. 39 ConfigureBasic() error 40 // ConfigureJuju updates the provided cloudinit.Config with configuration 41 // to initialise a Juju machine agent. 42 ConfigureJuju() error 43 } 44 45 // UserdataConfig is supposed to take in an instanceConfig as well as a 46 // cloudinit.cloudConfig and add attributes in the cloudinit structure based on 47 // the values inside instanceConfig and on the series 48 func NewUserdataConfig(icfg *instancecfg.InstanceConfig, conf cloudinit.CloudConfig) (UserdataConfig, error) { 49 // TODO(ericsnow) bug #1426217 50 // Protect icfg and conf better. 51 operatingSystem, err := version.GetOSFromSeries(icfg.Series) 52 if err != nil { 53 return nil, err 54 } 55 56 base := baseConfigure{ 57 tag: names.NewMachineTag(icfg.MachineId), 58 icfg: icfg, 59 conf: conf, 60 os: operatingSystem, 61 } 62 63 switch operatingSystem { 64 case version.Ubuntu: 65 return &unixConfigure{base}, nil 66 case version.CentOS: 67 return &unixConfigure{base}, nil 68 case version.Windows: 69 return &windowsConfigure{base}, nil 70 default: 71 return nil, errors.NotSupportedf("OS %s", icfg.Series) 72 } 73 } 74 75 type baseConfigure struct { 76 tag names.Tag 77 icfg *instancecfg.InstanceConfig 78 conf cloudinit.CloudConfig 79 os version.OSType 80 } 81 82 // addAgentInfo adds agent-required information to the agent's directory 83 // and returns the agent directory name. 84 func (c *baseConfigure) addAgentInfo(tag names.Tag) (agent.Config, error) { 85 acfg, err := c.icfg.AgentConfig(tag, c.icfg.Tools.Version.Number) 86 if err != nil { 87 return nil, errors.Trace(err) 88 } 89 acfg.SetValue(agent.AgentServiceName, c.icfg.MachineAgentServiceName) 90 cmds, err := acfg.WriteCommands(c.conf.ShellRenderer()) 91 if err != nil { 92 return nil, errors.Annotate(err, "failed to write commands") 93 } 94 c.conf.AddScripts(cmds...) 95 return acfg, nil 96 } 97 98 func (c *baseConfigure) addMachineAgentToBoot() error { 99 svc, err := c.icfg.InitService(c.conf.ShellRenderer()) 100 if err != nil { 101 return errors.Trace(err) 102 } 103 104 // Make the agent run via a symbolic link to the actual tools 105 // directory, so it can upgrade itself without needing to change 106 // the init script. 107 toolsDir := c.icfg.ToolsDir(c.conf.ShellRenderer()) 108 c.conf.AddScripts(c.toolsSymlinkCommand(toolsDir)) 109 110 name := c.tag.String() 111 cmds, err := svc.InstallCommands() 112 if err != nil { 113 return errors.Annotatef(err, "cannot make cloud-init init script for the %s agent", name) 114 } 115 startCmds, err := svc.StartCommands() 116 if err != nil { 117 return errors.Annotatef(err, "cannot make cloud-init init script for the %s agent", name) 118 } 119 cmds = append(cmds, startCmds...) 120 121 svcName := c.icfg.MachineAgentServiceName 122 // TODO (gsamfira): This is temporary until we find a cleaner way to fix 123 // cloudinit.LogProgressCmd to not add >&9 on Windows. 124 targetOS, err := version.GetOSFromSeries(c.icfg.Series) 125 if err != nil { 126 return err 127 } 128 if targetOS != version.Windows { 129 c.conf.AddRunCmd(cloudinit.LogProgressCmd("Starting Juju machine agent (%s)", svcName)) 130 } 131 c.conf.AddScripts(cmds...) 132 return nil 133 } 134 135 // TODO(ericsnow) toolsSymlinkCommand should just be replaced with a 136 // call to shell.Renderer.Symlink. 137 138 func (c *baseConfigure) toolsSymlinkCommand(toolsDir string) string { 139 switch c.os { 140 case version.Windows: 141 return fmt.Sprintf( 142 `cmd.exe /C mklink /D %s %v`, 143 c.conf.ShellRenderer().FromSlash(toolsDir), 144 c.icfg.Tools.Version, 145 ) 146 default: 147 // TODO(dfc) ln -nfs, so it doesn't fail if for some reason that 148 // the target already exists. 149 return fmt.Sprintf( 150 "ln -s %v %s", 151 c.icfg.Tools.Version, 152 shquote(toolsDir), 153 ) 154 } 155 } 156 157 func shquote(p string) string { 158 return utils.ShQuote(p) 159 }