jenkins-factory 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a11a17e3bdbd0d8632fefa847871616f6ad64a53
4
+ data.tar.gz: 4c89804b45d2267ceb3a258bd938853936bcb11b
5
+ SHA512:
6
+ metadata.gz: aca24b4ab3305044f841b24a98164e0e3fc5c1a28180a226606ee465e80efbdc66c53803e880b9f0b7cca16c21160965f6c2456f84a81ba5a006f7318ce0ed2a
7
+ data.tar.gz: f0ed381302fc519c3ce53fe8ba531965754bf996ae3e47322ff77dbd1ab7ceb2f7184ac1ad9312071ed85a15e0b5b8627dcef1ca44fa9d01b194b1881e677920
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ require 'trollop'
3
+ require 'jenkins_factory'
4
+ require 'yaml'
5
+
6
+ opts = Trollop::options do
7
+ opt :jenkins_settings_yml_path, 'Show the YAML here', type: :string, required: true
8
+ end
9
+
10
+ jenkins_settings = YAML.load_file(opts[:jenkins_settings_yml_path])
11
+
12
+ jenkins_settings.keys.each do |k|
13
+ jenkins_settings[k.to_sym] = jenkins_settings[k]
14
+ jenkins_settings.delete k
15
+ end
16
+
17
+ jenkins_url = JenkinsFactory.new.create(jenkins_settings)
18
+
19
+ puts jenkins_url
@@ -0,0 +1,20 @@
1
+ class BaseJenkinsAmi
2
+ REGION_BASE_AMIS = {
3
+ 'us-east-1' => 'ami-e2754888',
4
+ 'us-west-2' => 'ami-677c9e07'
5
+ }
6
+
7
+ def discover_base_ami
8
+ if ENV['AWS_REGION'].nil?
9
+ raise 'AWS_REGION must be set in environment'
10
+ else
11
+ base_ami = REGION_BASE_AMIS[ENV['AWS_REGION']]
12
+
13
+ if base_ami.nil?
14
+ raise "#{ENV['AWS_REGION']} is illegitimate or unsupported region"
15
+ else
16
+ base_ami
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,106 @@
1
+ CloudFormation {
2
+
3
+ IAM_Role('JenkinsInstanceRole') {
4
+ AssumeRolePolicyDocument JSON.load <<-END
5
+ {
6
+ "Statement":[
7
+ {
8
+ "Sid":"1",
9
+ "Effect":"Allow",
10
+ "Principal":{
11
+ "Service":[
12
+ "ec2.amazonaws.com"
13
+ ]
14
+ },
15
+ "Action":"sts:AssumeRole"
16
+ }
17
+ ]
18
+ }
19
+ END
20
+
21
+ Path '/'
22
+
23
+ Policies JSON.load <<-END
24
+ [
25
+ {
26
+ "PolicyName":"JenkinsGodlikeWreckYourAccountPolicy",
27
+ "PolicyDocument":{
28
+ "Version":"2012-10-17",
29
+ "Statement": [
30
+ {
31
+ "Action": [
32
+ "*"
33
+ ],
34
+ "Effect": "Allow",
35
+ "Resource": "*"
36
+ }
37
+ ]
38
+ }
39
+ }
40
+ ]
41
+ END
42
+ }
43
+
44
+ IAM_InstanceProfile('JenkinsInstanceProfile') {
45
+ Path '/'
46
+ Roles [ Ref('JenkinsInstanceRole') ]
47
+ }
48
+
49
+ EC2_SecurityGroup('JenkinsSecurityGroup') {
50
+ VpcId vpc_id
51
+ GroupDescription 'Will mostly be phoning home to CP'
52
+ }
53
+
54
+ %w(22 8080).each do |ingress_port|
55
+ EC2_SecurityGroupIngress("SecurityGroupIngress#{ingress_port}") {
56
+ GroupId Ref('JenkinsSecurityGroup')
57
+ IpProtocol 'tcp'
58
+ FromPort ingress_port.to_s
59
+ ToPort ingress_port.to_s
60
+ CidrIp jenkins_ingress_ssh_cidr
61
+ }
62
+ end
63
+
64
+ EC2_Instance('JenkinsInstance') {
65
+ ImageId jenkins_base_ami_id
66
+ InstanceType 'm4.large'
67
+ KeyName jenkins_ec2_key_pair_name
68
+
69
+ IamInstanceProfile Ref('JenkinsInstanceProfile')
70
+
71
+ NetworkInterfaces [
72
+ NetworkInterface {
73
+ GroupSet Ref('JenkinsSecurityGroup')
74
+ AssociatePublicIpAddress associate_public_ip_address
75
+ DeviceIndex 0
76
+ DeleteOnTermination true
77
+ SubnetId subnet_id
78
+ }
79
+ ]
80
+
81
+ start_jenkins_commands = [
82
+ "#!/bin/bash -xe\n",
83
+ "yum update -y aws-cfn-bootstrap\n",
84
+ "yum -y upgrade\n",
85
+ "service jenkins start\n"
86
+ ]
87
+
88
+ cfn_signal_commands = [
89
+ '/opt/aws/bin/cfn-signal -e $? ',
90
+ ' --stack ', Ref('AWS::StackName'),
91
+ ' --resource JenkinsInstance ',
92
+ ' --region ',Ref('AWS::Region'),"\n"
93
+ ]
94
+
95
+ userdata_commands = start_jenkins_commands + extra_userdata + cfn_signal_commands
96
+ UserData FnBase64(FnJoin(
97
+ '',
98
+ userdata_commands
99
+ ))
100
+
101
+ CreationPolicy('ResourceSignal', { 'Count' => 1, 'Timeout' => 'PT15M' })
102
+ }
103
+
104
+ Output(:JenkinsURL,
105
+ FnJoin('', [ 'http://', FnGetAtt('JenkinsInstance', 'PublicIp'), ':8080/']))
106
+ }
@@ -0,0 +1,63 @@
1
+ require 'cfndsl_converger'
2
+ require_relative 'base_jenkins_ami'
3
+ require_relative 'job_seeder'
4
+
5
+ class JenkinsFactory
6
+
7
+ def create(controller_ip:,
8
+ ec2_keypair_name:,
9
+ vpc_id:,
10
+ subnet_id:,
11
+
12
+ job_repo:,
13
+ job_repo_branch: 'master',
14
+ job_definition_relative_path:,
15
+
16
+ # this is currently baked into the AMI
17
+ jenkins_admin_username:,
18
+ jenkins_admin_password:,
19
+
20
+ # crude bash commands, but maybe consider scp a tar of cookbooks?
21
+ extra_user_data_provisioning: [],
22
+ associate_public_ip_address: true)
23
+
24
+ customisations = {
25
+ 'jenkins_base_ami_id' => BaseJenkinsAmi.new.discover_base_ami,
26
+ 'jenkins_ingress_ssh_cidr' => controller_ip,
27
+ 'jenkins_ec2_key_pair_name' => ec2_keypair_name,
28
+ 'subnet_id' => subnet_id,
29
+ 'vpc_id' => vpc_id,
30
+ 'associate_public_ip_address' => associate_public_ip_address.to_s,
31
+ 'extra_userdata' => extra_user_data_provisioning
32
+ }
33
+
34
+ stack_outputs = converge_jenkins_stack customisations: customisations
35
+
36
+ jenkins_connection_info = {
37
+ 'jenkins_url' => stack_outputs['JenkinsURL'],
38
+ 'jenkins_user' => jenkins_admin_username,
39
+ 'jenkins_pass' => jenkins_admin_password
40
+ }
41
+
42
+ jobs_location = {
43
+ 'job_repo' => job_repo,
44
+ 'job_repo_branch' => job_repo_branch,
45
+ 'job_definition_relative_path' => job_definition_relative_path
46
+ }
47
+
48
+ JobSeeder.new.seed_jobs jenkins_connection_info: jenkins_connection_info,
49
+ jobs_location: jobs_location
50
+
51
+ stack_outputs['JenkinsURL']
52
+ end
53
+
54
+ private
55
+
56
+ def converge_jenkins_stack(customisations:)
57
+ converger = CfndslConverger.new
58
+ outputs = converger.converge stack_name: "Jenkins-Factory-#{Time.now.to_i}",
59
+ path_to_stack: 'lib/cfndsl/jenkins_cfndsl.rb',
60
+ bindings: customisations
61
+ outputs
62
+ end
63
+ end
data/lib/job_seeder.rb ADDED
@@ -0,0 +1,40 @@
1
+ require 'jenkins_api_client'
2
+
3
+ # i guess also consider using local jenkins cli from user data?
4
+ class JobSeeder
5
+ def seed_jobs(jenkins_connection_info:,
6
+ jobs_location:)
7
+
8
+ client = jenkins_api_client jenkins_connection_info: jenkins_connection_info
9
+
10
+ job_name = 'job-seed'
11
+ if client.job.exists? job_name
12
+ client.job.delete job_name
13
+ end
14
+
15
+ seed_xml = patch_job_seed jobs_location: jobs_location
16
+
17
+ client.job.create job_name,
18
+ seed_xml
19
+
20
+ client.job.build job_name
21
+ end
22
+
23
+ # only exposed for testing, don't call me
24
+ # ok yea ERB more elegant but i'm feeling lazy
25
+ def patch_job_seed(jobs_location:)
26
+ seed_xml = IO.read(File.join('lib', 'xml', 'job-seed-config.xml.erb'))
27
+ seed_xml.gsub!(/<%= @job_definition_relative_path %>/, jobs_location['job_definition_relative_path'])
28
+ seed_xml.gsub!(/<%= @job_repo_branch %>/, jobs_location['job_repo_branch'])
29
+ seed_xml.gsub!(/<%= @job_repo %>/, jobs_location['job_repo'])
30
+ seed_xml
31
+ end
32
+
33
+ private
34
+
35
+ def jenkins_api_client(jenkins_connection_info:)
36
+ JenkinsApi::Client.new server_url: jenkins_connection_info['jenkins_url'],
37
+ username: jenkins_connection_info['jenkins_user'],
38
+ password: jenkins_connection_info['jenkins_pass']
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jenkins-factory
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - someguy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: trollop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-utils
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.0.5
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.0.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: jenkins_api_client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.4.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '='
53
+ - !ruby/object:Gem::Version
54
+ version: 1.4.2
55
+ description: Experimental single-click for cranking out a basic Jenkins instance in
56
+ EC2
57
+ email:
58
+ executables:
59
+ - jenkins_factory
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - bin/jenkins_factory
64
+ - lib/base_jenkins_ami.rb
65
+ - lib/cfndsl/jenkins_cfndsl.rb
66
+ - lib/jenkins_factory.rb
67
+ - lib/job_seeder.rb
68
+ homepage: https://github.com/stelligent/jenkins-factory
69
+ licenses:
70
+ - MIT
71
+ metadata: {}
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ - lib
77
+ required_ruby_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '2.2'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ requirements: []
88
+ rubyforge_project:
89
+ rubygems_version: 2.6.2
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: jenkins_factory
93
+ test_files: []