Skip to content

AWS Programming Guide

Chris Doherty edited this page Sep 28, 2015 · 15 revisions

chef-provisioning-aws Resource Programming Guide

Resource

You must (in general) declare the AWS SDK object to which your object corresponds:

aws_sdk_type ::Aws::Route53::Types::HostedZone

aws_sdk_type takes several options in a hash:

  • :load_provider => [Boolean] - ?
  • :id => [AWS SDK object field name] - Method on the AWS SDK object which retrieves the AWS-side ID. Default: :id.
  • :backcompat_data_bag_name => [String] - ?
  • :managed_entry_id_name => [String] - ?
  • :option_names => [Array] - This corresponds to the keys that will be automatically converted from resource name to aws id from the lookup_options method. By default it provides the snake-cased resource name and the snake-cased resource name with its :id field appended. For example, the AwsSubnet will get option names of subnet and subnet_id (because its id field is :id). When a hash contains a top level key of subnet or subnet_id and the lookup_options method is called on this, it will convert 'my_subnet_resource' to 'subnet-123456'. lookup_options(subnet_id: 'my_subnet_resource') == {subnet_id: 'subnet-123456'}.

Your resource must have an aws_object method which returns your corresponding AWS SDK object.

One of your resource attributes must be marked as the aws_id_attribute:

attribute :aws_route53_zone_id, kind_of: String, aws_id_attribute: true

If your resource as an AWSResourceWithEntry this aws_id_attribute must have a lazy default that converts the resource name to the id. For example,

attribute :volume_id, kind_of: String, aws_id_attribute: true, default: lazy {
  name =~ /^vol-[a-f0-9]{8}$/ ? name : nil
}

This is required because the ManagedEntry magic links the resource name to the aws object identifier. During the lookup phase the magic creates resources with names like "vol-123456" and uses these to find AWS objects. If there is not a lazy default converting the name to the aws_id_attribute this magic breaks.

Provider

Your provider will implement hook methods corresponding to actions. chef-provisioning-aws will dispatch to them as needed, and you can do whatever you like in them, but if you don't return the correct values, the errors will be tricky to track down.

  • create_aws_object - Obviously, create the AWS object itself. This method must return the actual AWS SDK object.

  • update_aws_object(aws_sdk_object)- Update the object if it already exists. Your code should not be in a converge_by block. If it is, the be_idempotent RSpec matcher will always report that your resource has been updated, and fail.

    • Many AWS objects cannot be updated, and you may destroy and re-create them instead; in that case, this method should return [:replaced_aws_object, new_aws_object] in order to correctly update the stored reference.
    • Otherwise, the return value is ignored.
  • destroy_aws_object(aws_sdk_object) - Destroy the AWS object and delete any managed entry (i.e. data bag) item that isn't handled automatically. Return value is ignored.

Managed Entries

There are possible variations here, but in the most common case of running with a Chef Server (including local mode/chef-client -z), Chef Provisioning will automatically store important information about your resource (like its AWS ID) in a data bag keyed on the resource's name, and will automatically update and delete that information if the resource changes.

aws_driver.rb

Add your resource file to the list of requires:

require "chef/resource/aws_sns_topic"

driver.rb

Testing Environment

chef-provisioning-aws auto-generates RSpec matchers for your AWS object which will save a lot of repetitive matching and cleanup code. Take the following example:

        it "creates a hosted zone with a RecordSet" do
          expect_recipe {
            aws_route53_hosted_zone "feegle.com" do
              action :create
              record_sets {
                aws_route53_record_set "some-hostname CNAME" do
                  rr_name "some-api-host.feegle.com"
                  type "CNAME"
                  ttl 3600
                  resource_records [{ value: "some-other-host"}]
                end
              }
            end
          }.to create_an_aws_route53_hosted_zone("feegle.com",
                                                 resource_record_sets: [{}, {}, { ttl: 3600 }])
        end
  • create_an_aws_route53_hosted_zone converges the recipe and allows you to examine the resulting SDK object in depth.
    • When it's done, it will call your resource's :destroy action. Since we assume SDK calls succeed if there's no error, this more or less tests your :destroy for you.
  • {} acts as a wildcard on the first 2 elements of the array returned by resource_record_sets: because they list no fields to check against the Route 53 SDK object, nothing is checked.
  • To enable this matching, add your AWS object type (ELB, Route53) to the array appended to DeepMatcher::MatchableObject.matchable_classes in spec/aws_support.rb.