Class: CfnGuardian::Deploy

Inherits:
Object
  • Object
show all
Includes:
Logging
Defined in:
lib/cfnguardian/deploy.rb

Instance Method Summary collapse

Methods included from Logging

colors, included, logger, #logger, logger=

Constructor Details

#initialize(opts, bucket, parameters, template_file, stack_name) ⇒ Deploy

Returns a new instance of Deploy.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/cfnguardian/deploy.rb', line 11

def initialize(opts,bucket,parameters,template_file,stack_name)
  @stack_name = stack_name
  @bucket = bucket
  @s3_path = "#{stack_name}/#{template_file}"
  @template_path = "out/#{template_file}"
  @template_url = "https://#{@bucket}.s3.amazonaws.com/#{@s3_path}"
  @parameters = parameters
  @changeset_role_arn = opts.fetch(:role_arn, nil)

  @tags = opts.fetch(:tags, {})
  if ENV.has_key?('CODEBUILD_RESOLVED_SOURCE_VERSION')
    @tags[:'guardian:config:commit'] = ENV['CODEBUILD_RESOLVED_SOURCE_VERSION']
  end

  @client = Aws::CloudFormation::Client.new()
end

Instance Method Details

#create_change_setObject



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/cfnguardian/deploy.rb', line 54

def create_change_set()
  change_set_name = "#{@stack_name}-#{CfnGuardian::CHANGE_SET_VERSION}-#{Time.now.utc.strftime("%Y%m%d%H%M%S")}"
  change_set_type = get_change_set_type()

  if change_set_type == 'CREATE'
    params = get_parameters_from_template()
  else
    params = get_parameters_from_stack()
  end

  params.each do |param|
    if !@parameters[param[:parameter_key].to_sym].nil?
      param[:parameter_value] = @parameters[param[:parameter_key].to_sym]
      param[:use_previous_value] = false
    end
  end

  tags = get_tags()
  logger.debug "tagging stack with tags #{tags}"

  changeset_request = {
    stack_name: @stack_name,
    template_url: @template_url,
    capabilities: ["CAPABILITY_IAM"],
    parameters: params,
    tags: tags,
    change_set_name: change_set_name,
    change_set_type: change_set_type
  }

  unless @changeset_role_arn.nil?
    changeset_request[:role_arn] = @changeset_role_arn
  end

  logger.debug "Creating changeset"
  change_set = @client.create_change_set(changeset_request)
  return change_set, change_set_type
end

#does_cf_stack_existObject

TODO: check for REVIEW_IN_PROGRESS



39
40
41
42
43
44
45
46
47
48
# File 'lib/cfnguardian/deploy.rb', line 39

def does_cf_stack_exist()
  begin
    resp = @client.describe_stacks({
      stack_name: @stack_name,
    })
  rescue Aws::CloudFormation::Errors::ValidationError
    return false
  end
  return resp.size > 0
end

#execute_change_set(change_set_id) ⇒ Object



113
114
115
116
117
118
# File 'lib/cfnguardian/deploy.rb', line 113

def execute_change_set(change_set_id)
  logger.debug "Executing the changeset"
  stack = @client.execute_change_set({
    change_set_name: change_set_id
  })
end

#get_change_set(change_set_id) ⇒ Object



107
108
109
110
111
# File 'lib/cfnguardian/deploy.rb', line 107

def get_change_set(change_set_id)
  @client.describe_change_set({
    change_set_name: change_set_id,
  })
end

#get_change_set_typeObject



50
51
52
# File 'lib/cfnguardian/deploy.rb', line 50

def get_change_set_type()
  return does_cf_stack_exist() ? 'UPDATE' : 'CREATE'
end

#get_parameters_from_stackObject



126
127
128
129
# File 'lib/cfnguardian/deploy.rb', line 126

def get_parameters_from_stack()
  resp = @client.get_template_summary({ stack_name: @stack_name })
  return resp.parameters.collect { |p| { parameter_key: p.parameter_key, use_previous_value: true }  }
end

#get_parameters_from_templateObject



131
132
133
134
135
# File 'lib/cfnguardian/deploy.rb', line 131

def get_parameters_from_template()
  template_body = File.read(@template_path)
  resp = @client.get_template_summary({ template_body: template_body })
  return resp.parameters.collect { |p| { parameter_key: p.parameter_key, parameter_value: p.default_value }  }
end

#get_tagsObject



137
138
139
140
141
142
143
144
145
# File 'lib/cfnguardian/deploy.rb', line 137

def get_tags()
  default_tags = {
    'guardian:version': CfnGuardian::VERSION,
    Environment: 'guardian'
  }
  default_tags.merge!(@tags)
  tags = default_tags.map {|k,v| {key: k, value: v}}
  return tags
end

#upload_templatesObject



28
29
30
31
32
33
34
35
36
# File 'lib/cfnguardian/deploy.rb', line 28

def upload_templates
  body = File.read(@template_path)
  client = Aws::S3::Client.new()
  client.put_object({
    body: body,
    bucket: @bucket,
    key: @s3_path
  })
end

#wait_for_changeset(change_set_id) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/cfnguardian/deploy.rb', line 93

def wait_for_changeset(change_set_id)
  logger.debug "Waiting for changeset to be created"
  begin
    @client.wait_until :change_set_create_complete, change_set_name: change_set_id
  rescue Aws::Waiters::Errors::FailureStateError => e
    change_set = get_change_set(change_set_id)
    if change_set.status_reason.include?("The submitted information didn't contain changes.") || change_set.status_reason.include?("No updates are to be performed") && @ignore_empty_change_set
      raise CfnGuardian::EmptyChangeSetError, "No changes to deploy. Stack #{@stack_name} is up to date"
    else
      raise CfnGuardian::ChangeSetError, "Failed to create the changeset : #{e.message} Status: #{change_set.status} Reason: #{change_set.status_reason}"
    end
  end
end

#wait_for_execute(change_set_type) ⇒ Object



120
121
122
123
124
# File 'lib/cfnguardian/deploy.rb', line 120

def wait_for_execute(change_set_type)
  waiter = change_set_type == 'CREATE' ? :stack_create_complete : :stack_update_complete
  logger.info "Waiting for changeset to #{change_set_type}"
  resp = @client.wait_until waiter, stack_name: @stack_name
end