Andrey Adamovich, Aestas/IT
Our take on:
Automate the provisioning and maintenance of servers:
Imagine uploading *.class files and repackaging JAR directly on production servers when you have an urgent code change.
ant.taskdef(name: 'scp',classname: 'o.a.t.a.t.o.ssh.Scp',classpath: configurations.secureShell.asPath)ant.taskdef(name: 'sshexec',classname: 'o.a.t.a.t.o.ssh.SSHExec',classpath: configurations.secureShell.asPath)
ant.sshexec(host: host,username: user,password: password,command: command,trust: 'true',failonerror: failOnError)
def ssh(String command,Properties props,boolean failOnError = false,String suCommandQuoteChar = "'",String outputProperty = null) {...}
def scp(String file,String remoteDir,Properties props) {...}
task installFonts << {forAllServers { props ->ssh('yes | yum install *font*', props)}}
task uninstallNginx << {forAllServers { props ->ssh('/etc/init.d/nginx stop', props)ssh('yes | yum remove nginx', props, true)ssh('rm -rf /etc/yum.repos.d/nginx.repo', props)ssh('rm -rf /var/log/nginx', props)ssh('rm -rf /etc/nginx /var/nginx', props)}}
Groovy-based SSH DSL for:
@Grab(group='com.aestasit.infrastructure.sshoogr',module='sshoogr',version='0.9.16')import static com.aestasit.ssh.DefaultSsh.*
defaultUser = 'root'defaultKeyFile = new File('secret.pem')execOptions {verbose = trueshowCommand = true}
remoteSession {url = 'user2:654321@localhost:2222'exec 'rm -rf /tmp/*'exec 'touch /var/lock/my.pid'remoteFile('/var/my.conf').text = "enabled=true"}
remoteFile('/etc/yum.repos.d/puppet.repo').text = '''[puppet]name=Puppet Labs Packagesbaseurl=http://yum.puppetlabs.com/el/enabled=0gpgcheck=0'''
remoteSession {scp {from { localDir "$buildDir/application" }into { remoteDir '/var/bea/domain/application' }}}
def result = exec(command: '/usr/bin/mycmd',failOnError: false, showOutput: false)if (result.exitStatus == 1) {result.output.eachLine { line ->if (line.contains('WARNING')) {throw new RuntimeException("Warning!!!")}}}
if (ok('/usr/bin/mycmd')) {...}if (fail('/usr/bin/othercmd')) {...}
tunnel('1.2.3.4', 8080) { int localPort ->def url = "http://localhost:${localPort}/flushCache"def result = new URL(url).textif (result == 'OK') {println "Cache is flushed!"} else {throw new RuntimeException(result)}}
prefix('sudo ') {exec 'rm -rf /var/log/abc.log'exec 'service abc restart'}suffix(' >> output.log') {exec 'yum -y install nginx'exec 'yum -y install mc'exec 'yum -y install links'}
Scripts are usually not idempotent
task uploadModules << {remoteSession {exec 'rm -rf /tmp/repo.zip'scp {from { localFile "${buildDir}/repo.zip" }into { remoteDir "/root" }}...
...exec 'rm -rf /etc/puppet/modules'exec 'unzip /tmp/repo.zip -d /etc/puppet/modules'}}
task puppetApply(dependsOn: uploadModules) << {remoteSession {scp {from { localFile "${buildDir}/setup.pp" }into { remoteDir "/tmp" }}exec 'puppet apply /tmp/setup.pp'}}
class DerbyInstallTestextends BasePuppetIntegrationTest {@Beforevoid installDerby() {apply("include derby")}...}
@Testvoid ensureDerbyRunning() {command('service derby status > derbystatus.log')assertTrue fileText("/root/derbystatus.log").contains('Derby')assertTrue fileText("/root/derbystatus.log").contains('is running.')}
@Testvoid ensureCanConnect() {Thread.sleep(10000)uploadScript()command('/opt/derby/db-derby-10.9.1.0-bin/bin/ij ' +'testDataScript.sql > derbytest.log')...
...// Check if the log of the insert// operation contains the word ERROR.assertFalse("The script should return at least one error",fileText("/root/derbytest.log").contains('ERROR'))...
...// Check on data that was inserted into a table.assertTrue("The log should contain a SELECT result",fileText("/root/derbytest.log").contains('Grand Ave.'))}
session {tunnel ('127.0.0.1', 8080) { int localPort ->def driver = new HtmlUnitDriver(false)driver.manage().timeouts().pageLoadTimeout(300, TimeUnit.SECONDS).implicitlyWait(30, TimeUnit.SECONDS)driver.get("http://127.0.0.1:${localPort}/login")...
...def input = driver.findElement(By.name('j_username'))input.sendKeys('john')input = driver.findElement(By.name('j_password'))input.sendKeys('123456')input.submit()...
...def wait = new WebDriverWait(driver, 30)wait.until ExpectedConditions.presenceOfElementLocated (By.linkText('John Doe'))...}}
session {tunnel ('127.0.0.1', 80) { int localPort ->// Initilize repository connection data.DAVRepositoryFactory.setup()def url = SVNURL.create('http', null, '127.0.0.1',localPort, 'repos/cafebabe', true)def repository = SVNRepositoryFactory.create(url)println "Verifying SVN repository at ${url}"...
...// Setup credentials.def authManager = SVNWCUtil.createDefaultAuthenticationManager('joe', '123456')repository.setAuthenticationManager(authManager)// Verify repository is at revision 0.assertEquals 0, repository.getLatestRevision()...
...// Commit first revision.ISVNEditor editor = repository.getCommitEditor("Initial commit.", null)editor.with {openRoot(-1)addFile('dummy.txt', null, -1)applyTextDelta('dummy.txt', null)def deltaGenerator = new SVNDeltaGenerator()
...def checksum = deltaGenerator.sendDelta('dummy.txt',new ByteArrayInputStream("data".getBytes()),editor, true)closeFile('dummy.txt', checksum)def commitInfo = closeEdit()println commitInfo}...
...// Verify repository is at revision 1 now.assertEquals 1, repository.getLatestRevision()}}
task startInstance(type: StartInstance) {keyName 'cloud-do'securityGroup 'cloud-do'instanceName 'gramazon/cloud-do'stateFileName 'cloud-do.json'ami 'ami-6f07e418'instanceType 't1.micro'waitForStart true}
task terminateInstance(type: TerminateInstance) {stateFileName 'cloud-do.json'}