diff --git a/README.md b/README.md index 72787d60..825232f9 100644 --- a/README.md +++ b/README.md @@ -4,21 +4,41 @@ This software is still ALPHA quality. The APIs will be likely to change. -## Usage +## Quick Start AWS-SDK provides interfaces for each AWS services as individual systems under "aws-sdk/services/*". Here's an example to send an SMS via Amazon Simple Notification Service: ```common-lisp -(ql:quickload '(:aws-sdk :aws-sdk/services/sns)) +;; You must load aws-sdk first, then you can load services +(ql:quickload :aws-sdk) +;; Services can be loaded by name with quicklisp +(ql:quickload :aws-sdk/services/sns) + +;; ...or via aws-load +(aws-load sns) + +;; "Log in" to AWS by setting *session* via make-session (setf aws:*session* (aws:make-session)) ;; Sending (aws/sns:publish :phone-number "+8190xxxxxxxx" :message "Hi, there") ``` +You can also configure AWS-SDK to use a configured profile for authentication: +``` +(setf aws:*session* (aws:make-session :profile "profile_name_here")) +``` + +You can _also_ also use the `LOG-IN` macro as shorthand: + +``` common-lisp +(log-in :profile "profile_name_here") +``` + + ## Configuring the SDK ### Configuring Credentials @@ -29,8 +49,7 @@ Before using the SDK, you'll need to set AWS credentials for AWS services. AWS-S * Shared Credentials file (~/.aws/credentials). * EC2 Instance Role Credentials -It's also can be configured via `aws:*session*`: - +To "log in" to AWS, you must set the `*session*` variable with a session containing your credentials, and any configuration. This can be done via the `make-session` function. ```common-lisp (setf aws:*session* (aws:make-session :credentials @@ -56,11 +75,19 @@ It's also can be configured via `aws:*session*`: ## Development note ### Generating all services +From a Common Lisp REPL, with the current working directory set to the root of aws-sdk-lisp: +``` +(ql:quickload :lake) +(lake:lake) +``` +Or, from a command-line with ``` $ lake ``` +See: [github.com/takagi/lake](https://github.com/takagi/lake) + ## Author * Eitaro Fukamachi (e.arrows@gmail.com) diff --git a/make-session.lisp b/make-session.lisp index 5f328de4..fa7f22d2 100644 --- a/make-session.lisp +++ b/make-session.lisp @@ -1,6 +1,7 @@ (defpackage #:aws-sdk/make-session (:use #:cl) (:import-from #:aws-sdk/session + #:*session* #:%make-session) (:import-from #:aws-sdk/credentials #:credentials @@ -24,7 +25,8 @@ #:getenv) (:import-from #:assoc-utils #:aget) - (:export #:make-session)) + (:export #:make-session + #:log-in)) (in-package #:aws-sdk/make-session) (defun make-session (&key credentials region (profile *aws-profile*)) @@ -51,3 +53,6 @@ :region (or region (shared-config-region shared-config) (getenv "AWS_REGION"))))) + +(defmacro log-in (&rest args) + `(setf *session* (funcall #'make-session ,@args))) diff --git a/shared-config.lisp b/shared-config.lisp index 38d6333e..b56de7e4 100644 --- a/shared-config.lisp +++ b/shared-config.lisp @@ -3,6 +3,7 @@ (:import-from #:aws-sdk/credentials #:make-credentials) (:import-from #:aws-sdk/utils/config + #:*aws-profile* #:read-from-file) (:import-from #:assoc-utils #:aget) @@ -11,6 +12,7 @@ #:shared-config-credentials #:shared-config-region #:shared-config-assume-role + #:shared-config-profile #:assume-role-config #:assume-role-role-arn @@ -36,59 +38,77 @@ (user-homedir-pathname))) (config-path (merge-pathnames ".aws/config" (user-homedir-pathname))) - profile + (profile *aws-profile*) region credentials (assume-role nil :type (or assume-role-config null))) (defun make-shared-config (&rest args &key credentials-path config-path profile) - (declare (ignore credentials-path config-path)) - (let ((shared-config (apply #'%make-shared-config args))) - (let ((section (with-slots (credentials-path profile) shared-config - (when (and credentials-path - (probe-file credentials-path)) - (read-from-file credentials-path - :profile profile - :allow-no-profile t))))) - (let ((access-key-id (aget section "aws_access_key_id")) - (secret-access-key (aget section "aws_secret_access_key"))) - (when (and access-key-id secret-access-key) - (setf (shared-config-credentials shared-config) - (make-credentials - :access-key-id access-key-id - :secret-access-key secret-access-key - :session-token (aget section "aws_session_token") - :provider-name (format nil "shared-config: ~A" - (shared-config-config-path shared-config)))))) + (declare (ignorable credentials-path config-path profile)) + (let* ((shared-config (apply #'%make-shared-config args)) + (base-config-section (with-slots (config-path profile) shared-config + (when (and config-path + (probe-file config-path)) + (read-from-file config-path + :profile profile + :allow-no-profile t)))) + (base-creds-section (with-slots (credentials-path profile) shared-config + (when (and credentials-path + (probe-file credentials-path)) + (read-from-file credentials-path + :profile profile + :allow-no-profile t)))) + (source-profile-name (aget base-config-section "source_profile")) + (source-config-section (when source-profile-name + (with-slots (config-path) shared-config + (when (and config-path + (probe-file config-path)) + (read-from-file config-path + :profile source-profile-name))))) + (source-creds-section (when source-profile-name + (with-slots (credentials-path) shared-config + (when (and credentials-path + (probe-file credentials-path)) + (read-from-file credentials-path + :profile source-profile-name))))) + (role-arn (aget base-config-section "role_arn")) - (let ((role-arn (aget section "role_arn")) - (source-profile (aget section "source_profile")) - (credential-source (aget section "credential_source"))) - (when (and role-arn - (or source-profile credential-source)) - (when (and source-profile - credential-source) - (error "Profile ~S has both of source_profile and credential_source" - profile)) - (when credential-source - (error "credential_source is not supported yet")) - (setf (shared-config-assume-role shared-config) - (make-assume-role-config :role-arn role-arn - :source-profile source-profile - :credential-source credential-source - :external-id (aget section "external_id") - :serial-number (aget section "mfa_serial") - :role-session-name (aget section "role_session_name")))))) + ;; You cannot specify both source_profile and credential_source in the same profile. + (credential-source (if (and (aget base-config-section "credential_source") source-profile-name) + (error "You cannot specify both source_profile and credential_source in the same profile") + (aget base-config-section "credential_source"))) + (access-key-id (or (aget base-creds-section "aws_access_key_id") + (aget source-creds-section "aws_access_key_id"))) + (secret-access-key (or (aget base-creds-section "aws_secret_access_key") + (aget source-creds-section "aws_secret_access_key"))) + ;; I'm not sure if this is in the actual standard + (region (or (aget base-config-section "region") + (aget source-config-section "region")))) - (let ((section (with-slots (config-path profile) shared-config - (when (and config-path - (probe-file config-path)) - (read-from-file config-path - :profile profile - :allow-no-profile t))))) - (let ((region (aget section "region"))) - (when region - (setf (shared-config-region shared-config) region)))) + (when (and credential-source (not (member credential-source '("Environment" "Ec2InstanceMetadata" "EcsContainer")))) + (error "Invalid credential_source")) + + (when (and role-arn + (or source-profile-name credential-source)) + (setf (shared-config-assume-role shared-config) + (make-assume-role-config :role-arn role-arn + :source-profile source-profile-name + :credential-source credential-source + :external-id (aget base-config-section "external_id") + :serial-number (aget base-config-section "mfa_serial") + :role-session-name (aget base-config-section "role_session_name")))) + (when (and access-key-id secret-access-key) + (setf (shared-config-credentials shared-config) + (make-credentials + :access-key-id access-key-id + :secret-access-key secret-access-key + :session-token (or (aget base-creds-section "aws_session_token") + (aget source-creds-section "aws_session_token")) + :provider-name (format nil "shared-config: ~A" + (shared-config-config-path shared-config))))) + + (when region + (setf (shared-config-region shared-config) region)) shared-config))