Skip to content

Commit

Permalink
Working cookbook package/cleanup and supermarket share. S3 share stubbed
Browse files Browse the repository at this point in the history
  • Loading branch information
jmanero committed May 1, 2015
0 parents commit f6b8273
Show file tree
Hide file tree
Showing 16 changed files with 403 additions and 0 deletions.
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/
*.bundle
*.so
*.o
*.a
mkmf.log
VERSION
18 changes: 18 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
AllCops:
Exclude:
- libraries/**/*
- spec/**/*
- metadata.rb

Encoding:
Enabled: false
LineLength:
Enabled: false
HashSyntax:
Enabled: false
RescueModifier:
Enabled: false
MethodLength:
Max: 24
AbcSize:
Max: 60
4 changes: 4 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source 'https://rubygems.org'

# Specify your gem's dependencies in chef_life.gemspec
gemspec
22 changes: 22 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Copyright (c) 2015 jmanero

MIT License

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Chef Life
Humane lifecycle management for cookbooks
5 changes: 5 additions & 0 deletions Thorfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# encoding: utf-8

require 'bundler'
require 'bundler/setup'
require 'thor/scmversion'
3 changes: 3 additions & 0 deletions bin/cl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
require_relative '../lib/chef_life/cli'

ChefLife::CLI.start(ARGV)
29 changes: 29 additions & 0 deletions chef_life.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)

require 'chef_life/version'

Gem::Specification.new do |spec|
spec.name = 'chef_life'
spec.version = ChefLife::VERSION
spec.authors = ['jmanero']
spec.email = ['[email protected]']
spec.summary = 'Humane lifecycle management for cookbooks'
spec.description = IO.read(File.expand_path('../README.md', __FILE__))
spec.homepage = 'https://github.com/jmanero/chef_life'
spec.license = 'MIT'

spec.files = `git ls-files`.split("\n")
spec.executables = spec.files.grep(/^bin\//) { |f| File.basename(f) }
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
spec.require_paths = ['lib']

spec.add_dependency 'aws-sdk', '~> 2.0'
spec.add_dependency 'chef', '~> 12.0'
spec.add_dependency 'ignorefile'
spec.add_dependency 'thor'
spec.add_dependency 'thor-scmversion', '1.7.0'

spec.add_development_dependency 'bundler', '~> 1.7'
end
39 changes: 39 additions & 0 deletions lib/chef_life.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'chef'
require 'thor'

## Patch ThorScmversion#current to return the version string
require_relative './thor_scmversion'

require_relative './chef_life/task/cookbook'
require_relative './chef_life/task/s3'
require_relative './chef_life/task/supermarket'
##
# ___+++________00++00______________
# ___+000________0++00______________
# ____++00_______++000_____+0_______
# ____++00______+++00_____+00_______
# _____++00_____++000+____+00_______
# _____++00____++000+0____++0_______
# _____++000___++888+00__++00_______
# ______++000_++888++000++00________
# ______++00000++00+00000+00________
# ______+++0000+000000000000________
# _______++00000000000000000________
# _______+++000000000000000_________
# ______+0+++00000000000000_________
# _____+0++++0000000000000__________
# ______+00+++000000000000__________
# _______++0++000000000000__________
# ________+++000000000000___________
# _________++000000000000___________
#
# I didn't choose the ChefLife. The ChefLife chose me.
##
module ChefLife
class << self
## Load kinfe configuration
def configure(file)
Chef::Config.from_file(file)
end
end
end
46 changes: 46 additions & 0 deletions lib/chef_life/cli.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require_relative '../chef_life'
require_relative '../chef_life/version'

module ChefLife
##
# Construct a CLI
##
class CLI < Thor
class_option :config, :type => :string,
:aliases => :c,
:default => File.join(ENV['HOME'], '.chef/knife.rb')
def initialize(*_)
super
ChefLife.configure(options['config'])
end

desc 'about', 'State your purpose!'
def about
say <<-THUG
_
.!.!.
! ! I THINK I SHALL SHIT, SHOWER
; : AND THEN GO GET SOME SODA
; :
;_____: {{{{{ CHEF LIFE }}}}}
! Coca!
!_____!
: :
: ;
.' '.
: :
'''''
THUG
say "The Chef Life. Version #{ ChefLife::VERSION }"
end

desc 'cookbook SUBCOMMAND ... ARGS', 'Cookbook tasks'
subcommand :cookbook, ChefLife::Cookbook

desc 's3 SUBCOMMAND ... ARGS', 'S3 tasks'
subcommand :s3, ChefLife::S3

desc 'supermarket SUBCOMMAND ... ARGS', 'Supermarket tasks'
subcommand :supermarket, ChefLife::Supermarket
end
end
83 changes: 83 additions & 0 deletions lib/chef_life/task/cookbook.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
require 'chef/cookbook/metadata'
require 'fileutils'
require 'ignorefile'
require 'securerandom'
require 'thor'
require 'thor-scmversion'

module ChefLife
##
# Do cookbook things
##
class Cookbook < Thor
include Thor::Actions
namespace :cookbook

attr_reader :name
attr_reader :version
attr_reader :tarball
attr_reader :temp_id
attr_reader :temp_dir

## Don't vendor VCS files.
## Reference GNU tar --exclude-vcs: https://www.gnu.org/software/tar/manual/html_section/tar_49.html
## Boosted from https://github.com/berkshelf/berkshelf/blob/master/lib/berkshelf/berksfile.rb
EXCLUDED_VCS_FILES = [
'.arch-ids', '{arch}', '.bzr', '.bzrignore', '.bzrtags',
'CVS', '.cvsignore', '_darcs', '.git', '.hg', '.hgignore',
'.hgrags', 'RCS', 'SCCS', '.svn', '**/.git', '.temp'].freeze

desc 'package', 'Prepare a cookbook for '
def package
@temp_id = SecureRandom.hex(16)
@temp_dir = File.join('.temp/', @temp_id)

ignore_file = IgnoreFile.new('chefignore', '.gitignore', EXCLUDED_VCS_FILES)
cookbook = Chef::Cookbook::Metadata.new

invoke :version, :current, []
cookbook.from_file('metadata.rb')

@name = cookbook.name
@version = cookbook.version

tarball_name = "#{ cookbook.name }-#{ cookbook.version }.tgz"
@tarball = File.join(@temp_dir, tarball_name)

## Place to assemble files to be tar'd
cookbook_stage = File.join(@temp_dir, cookbook.name)
empty_directory cookbook_stage

cookbook_files = Dir.glob('**/{*,.*}')
ignore_file.apply!(cookbook_files)

say_status :package, "Cookbook #{ cookbook.name }@#{ cookbook.version }"

## First, make directories
cookbook_files.select { |f| File.directory?(f) }.each do |f|
FileUtils.mkdir_p(File.join(cookbook_stage, f))
end

## Then hard-link files
cookbook_files.reject { |f| File.directory?(f) }.each do |f|
FileUtils.ln(f, File.join(cookbook_stage, f))
end

## Finally, write metadata.json
IO.write(File.join(cookbook_stage, 'metadata.json'), cookbook.to_json)

## And package it all up.
inside(@temp_dir) do
run "tar -czf #{ tarball_name } #{ cookbook.name }"
end

self
end

desc 'cleanup', 'Cleanup temporary files'
def cleanup(temp = nil)
return remove_dir temp unless temp.nil?
Dir.glob('.temp/*').each { |t| remove_dir t }
end
end
end
51 changes: 51 additions & 0 deletions lib/chef_life/task/s3.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'aws-sdk'
require 'securerandom'
require 'thor'

module ChefLife
##
# Do supermarket things
##
class S3 < Thor
include Thor::Actions
namespace :s3

desc 'share', 'Package and upload cookbook to a supermarket'
option :aws_key, :type => :string,
:aliases => :k,
:default => Chef::Config.knife['aws_secret_key_id']
option :aws_secret, :type => :string,
:aliases => :s,
:default => Chef::Config.knife['aws_secret_access_key']
option :bucket, :type => :string,
:aliases => :k,
:default => Chef::Config.knife['aws_cookbook_bucket']
option 'dry-run', :type => :boolean, :default => false
def share
## Package the cookbook. We retrun `self` from Cookbook#package:
cookbook = invoke(:cookbook, :package, nil, [])

say_status :upload, "Cookbook #{ cookbook.name }@#{ cookbook.version } "\
"to #{ options['bucket'] }"

if options['dry-run']
say_status 'dry-run', 'Cookbook is not uploaded in dry-run mode. Created '\
"#{ cookbook.tarball }. Run the clean task to remove temporary files.", :yellow
else
# http_resp = Chef::CookbookSiteStreamingUploader.post(
# File.join(options['site'], '/api/v1/cookbooks'),
# options['user'], options['key'],
# :tarball => File.open(cookbook.tarball),
# :cookbook => { :category => '' }.to_json
# )
#
# if http_resp.code.to_i != 201
# say_status :error, "Error uploading cookbook: #{ http_resp.code } #{ http_resp.message }", :red
# say http_resp.body
# end
end
ensure
invoke :cookbook, :cleanup, nil, [cookbook.temp_dir] unless options['dry-run']
end
end
end
53 changes: 53 additions & 0 deletions lib/chef_life/task/supermarket.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
require 'chef'
require 'chef/cookbook_site_streaming_uploader'
require 'securerandom'
require 'thor'

module ChefLife
##
# Do supermarket things
##
class Supermarket < Thor
include Thor::Actions
namespace :supermarket

desc 'share', 'Package and upload cookbook to a supermarket'
class_option :site, :type => :string,
:aliases => :s,
:default => Chef::Config.knife['supermarket_site'] || 'https://supermarket.chef.io/'
class_option :user, :type => :string,
:aliases => :u,
:default => Chef::Config.knife['supermarket_user'] || Chef::Config.node_name
class_option :key, :type => :string,
:aliases => :k,
:default => Chef::Config.knife['supermarket_key'] || Chef::Config.client_key

option 'dry-run', :type => :boolean, :default => false
def share
## Package the cookbook. We retrun `self` from Cookbook#package:
cookbook = invoke(:cookbook, :package, nil, [])

say_status :upload, "Cookbook #{ cookbook.name }@#{ cookbook.version } "\
"to #{ options['site'] } as #{ options['user'] }"

if options['dry-run']
say_status 'dry-run', 'Cookbook is not uploaded in dry-run mode. Created '\
"#{ cookbook.tarball }. Run the clean task to remove temporary files.", :yellow
else
http_resp = Chef::CookbookSiteStreamingUploader.post(
File.join(options['site'], '/api/v1/cookbooks'),
options['user'], options['key'],
:tarball => File.open(cookbook.tarball),
:cookbook => { :category => '' }.to_json
)

if http_resp.code.to_i != 201
say_status :error, "Error uploading cookbook: #{ http_resp.code } #{ http_resp.message }", :red
say http_resp.body
end
end
ensure
invoke :cookbook, :cleanup, nil, [cookbook.temp_dir] unless options['dry-run']
end
end
end
6 changes: 6 additions & 0 deletions lib/chef_life/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#
module ChefLife
VERSION = IO.read(
File.expand_path(
'../../../VERSION', __FILE__)) rescue '0.0.1'
end
Loading

0 comments on commit f6b8273

Please sign in to comment.