diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..33a2481 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,41 @@ +# Ruby CircleCI 2.0 configuration file +# +# Check https://circleci.com/docs/2.0/language-ruby/ for more details +# +version: 2 +jobs: + build: + docker: + # Specify the Ruby version you desire here + - image: circleci/ruby:2.3.5 + + working_directory: ~/identity-hostdata + steps: + - checkout + + - restore-cache: + key: identity-hostdata-{{ checksum "Gemfile.lock" }} + + - run: + name: Install dependencies + command: | + gem install bundler + bundle install --deployment --jobs=4 --retry=3 --without deploy development doc production --path vendor/bundle + + # Store bundle cache + - save-cache: + key: identity-hostdata-{{ checksum "Gemfile.lock" }} + paths: + - vendor/bundle + + - run: + name: Run Tests + command: | + make test + + # collect reports + - store_test_results: + path: /tmp/test-results + - store_artifacts: + path: /tmp/test-results + destination: test-results diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8eb3b06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/.bundle/ +/.yardoc +/Gemfile.lock +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/tmp/ + +# rspec failure tracking +.rspec_status diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..8c18f1a --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a3c82e9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +sudo: false +language: ruby +rvm: + - 2.3.5 +before_install: gem install bundler -v 1.15.4 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e429041 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# 0.1.0 (2017-09-21) + +- Added `Identity::Hostdata.domain` +- Added `Identity::Hostdata.env` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e71175f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,90 @@ +## Welcome! + +We're so glad you're thinking about contributing to an 18F open source project! +If you're unsure about anything, just ask — or submit the issue or pull request +anyway. The worst that can happen is you'll be politely asked to change +something. We love all friendly contributions. + +We want to ensure a welcoming environment for all of our projects. Our staff +follow the [18F Code of Conduct][code] and all contributors should do the same. + +We encourage you to read this project's CONTRIBUTING policy (you are here), its +[LICENSE](LICENSE.md), and its [README](README.md). + +If you have any questions or want to read more, check out the +[18F Open Source Policy GitHub repository][os-policy], or just +[shoot us an email](mailto:18f@gsa.gov). + +[code]: https://github.com/18F/code-of-conduct/blob/master/code-of-conduct.md +[os-policy]: https://github.com/18f/open-source-policy + +## Pull request guidelines + +Please follow our [Git Protocol][git] and [Code Review][review] guidelines. +[Glen Sanford's thoughts on code reviews][thoughts] are also well worth +reading. + +[git]: https://github.com/18F/development-guide/tree/master/git_protocol +[review]: https://github.com/18F/development-guide/tree/master/code_review +[thoughts]: http://glen.nu/ramblings/oncodereview.php + +### Commit message style guide: + +- Write your commit message summary in the imperative: "Fix bug" and not +"Fixed bug" or "Fixes bug." This convention matches up with commit messages +generated by commands like `git merge` and `git revert`. + +- Under the summary, start by explaining why this change is necessary, and +add details to help the person reviewing your code understand what your +pull request is about. + +- If the pull request fixes a GitHub issue, mention it at the bottom using +GitHub's syntax, such as `Fixes #123`. + +Example: + +``` +Load seed using before(:suite) in RSpec config + +**Why**: +- Loading the seed in a `before(:each)` block results in an unnecessary +database call before every single test, slowing down the test suite, +and making development less efficient. + +**How**: +- Use `before(:suite)` instead, since the data that is loaded is not +meant to change, and so that only one database call is made. +- To prevent the data from being wiped out after each spec, configure +Database Cleaner to ignore those static tables. + +Fixes #123 +``` + +Note that we use [Overcommit] to enforce some of the commit message rules. +If this is your first time contributing to this repo, you will need to +sign your Overcommit configuration by running `overcommit --sign` before +being able to run `git commit`. See the [Security] section in the Overcommit +README for more details. + +[Overcommit]: https://github.com/brigade/overcommit +[Security]: https://github.com/brigade/overcommit#security + +### Additional notes on pull requests and code reviews + +- Prioritize code reviews above your other work +- Review pull requests within 24 hours of being opened +- Keep pull requests as small as possible, and focused on a single topic +- Once a pull request is good to go, the code reviewer is the one who merges + it, then deletes the branch. + +## Public domain + +This project is in the public domain within the United States, and +copyright and related rights in the work worldwide are waived through +the [CC0 1.0 Universal public domain dedication][CC0]. + +All contributions to this project will be released under the CC0 +dedication. By submitting a pull request, you are agreeing to comply +with this waiver of copyright interest. + +[CC0]: https://creativecommons.org/publicdomain/zero/1.0/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..87d7942 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +# Specify your gem's dependencies in identity-hostdata.gemspec +gemspec diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..19fa33d --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,31 @@ +As a work of the United States Government, this project is in the +public domain within the United States. + +Additionally, we waive copyright and related rights in the work +worldwide through the CC0 1.0 Universal public domain dedication. + +## CC0 1.0 Universal Summary + +This is a human-readable summary of the [Legal Code (read the full text)](https://creativecommons.org/publicdomain/zero/1.0/legalcode). + +### No Copyright + +The person who associated a work with this deed has dedicated the work to +the public domain by waiving all of his or her rights to the work worldwide +under copyright law, including all related and neighboring rights, to the +extent allowed by law. + +You can copy, modify, distribute and perform the work, even for commercial +purposes, all without asking permission. + +### Other Information + +In no way are the patent or trademark rights of any person affected by CC0, +nor are the rights that other persons may have in the work or in how the +work is used, such as publicity or privacy rights. + +Unless expressly stated otherwise, the person who associated a work with +this deed makes no warranties about the work, and disclaims liability for +all uses of the work, to the fullest extent permitted by applicable law. +When using or citing the work, you should not imply endorsement by the +author or the affirmer. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6da396f --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +setup: + bin/setup + +test: setup + bundle exec rake spec diff --git a/README.md b/README.md new file mode 100644 index 0000000..fabc450 --- /dev/null +++ b/README.md @@ -0,0 +1,43 @@ +# Identity::Hostdata + +A gem to help read configuration from login.gov infrastructure + +## Installation + +Add this line to your application's Gemfile: + +```ruby +gem 'identity-hostdata', github: '18F/identity-hostdata' +``` + +## Usage + +Use this gem to access config data on a per-host basis + +``` +require 'identity/hostdata' + +Identity::Hostdata.domain +# => "login.gov" +``` + +## Development + +Run tests: + +``` +make test +``` + +## Contributing + +See [CONTRIBUTING](CONTRIBUTING.md) for additional information. + +## Public domain + +This project is in the worldwide [public domain](LICENSE.md). As stated in [CONTRIBUTING](CONTRIBUTING.md): + +> This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/). +> +> All contributions to this project will be released under the CC0 dedication. By submitting a pull request, you are agreeing to comply with this waiver of copyright interest. + diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..b7e9ed5 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require "bundler/gem_tasks" +require "rspec/core/rake_task" + +RSpec::Core::RakeTask.new(:spec) + +task :default => :spec diff --git a/bin/console b/bin/console new file mode 100755 index 0000000..098b927 --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require "bundler/setup" +require "identity/hostdata" + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require "irb" +IRB.start(__FILE__) diff --git a/bin/setup b/bin/setup new file mode 100755 index 0000000..dce67d8 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here diff --git a/identity-hostdata.gemspec b/identity-hostdata.gemspec new file mode 100644 index 0000000..a155bf2 --- /dev/null +++ b/identity-hostdata.gemspec @@ -0,0 +1,38 @@ +# coding: utf-8 +lib = File.expand_path("../lib", __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require "identity/hostdata/version" + +Gem::Specification.new do |spec| + spec.name = "identity-hostdata" + spec.version = Identity::Hostdata::VERSION + spec.authors = ["Zach Margolis"] + spec.email = ["zachary.margolis@gsa.gov"] + + spec.summary = %q{Library to help read host data for login.gov} + spec.description = %q{Common helpers across login.gov infrastructure} + spec.homepage = "https://github.com/18F/identity-hostdata" + spec.license = "LICENSE.md" + + # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' + # to allow pushing to a single host or delete this section to allow pushing to any host. + if spec.respond_to?(:metadata) + spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'" + else + raise "RubyGems 2.0 or newer is required to protect against " \ + "public gem pushes." + end + + spec.files = `git ls-files -z`.split("\x0").reject do |f| + f.match(%r{^(test|spec|features)/}) + end + spec.bindir = "exe" + spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } + spec.require_paths = ["lib"] + + spec.add_development_dependency "bundler", "~> 1.15" + spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rspec", "~> 3.0" + + spec.add_development_dependency "fakefs", "~> 0.11" +end diff --git a/lib/identity/hostdata.rb b/lib/identity/hostdata.rb new file mode 100644 index 0000000..d004b59 --- /dev/null +++ b/lib/identity/hostdata.rb @@ -0,0 +1,39 @@ +require "identity/hostdata/version" + +module Identity + module Hostdata + class MissingConfigError < StandardError; end + + CONFIG_DIR = '/etc/login.gov' + DOMAIN_PATH = File.join(CONFIG_DIR, 'info/domain') + ENV_PATH = File.join(CONFIG_DIR, 'info/env') + + def self.domain + @domain ||= begin + File.read(DOMAIN_PATH).chomp + rescue Errno::ENOENT => err + raise MissingConfigError, err.message if in_datacenter? + end + end + + def self.env + @env ||= begin + File.read(ENV_PATH).chomp + rescue Errno::ENOENT => err + raise MissingConfigError, err.message if in_datacenter? + end + end + + def self.in_datacenter? + File.directory?(CONFIG_DIR) + end + + # @api private + # Used to clear memoized values (intended for specs) + def self.reset! + instance_variables.each do |variable| + remove_instance_variable(variable) + end + end + end +end diff --git a/lib/identity/hostdata/version.rb b/lib/identity/hostdata/version.rb new file mode 100644 index 0000000..d222064 --- /dev/null +++ b/lib/identity/hostdata/version.rb @@ -0,0 +1,5 @@ +module Identity + module Hostdata + VERSION = "0.1.0" + end +end diff --git a/spec/identity/hostdata_spec.rb b/spec/identity/hostdata_spec.rb new file mode 100644 index 0000000..03d5375 --- /dev/null +++ b/spec/identity/hostdata_spec.rb @@ -0,0 +1,89 @@ +require "spec_helper" + +RSpec.describe Identity::Hostdata do + it "has a version number" do + expect(Identity::Hostdata::VERSION).not_to be nil + end + + context 'reading config from the filesystem' do + around(:each) do |ex| + Identity::Hostdata.reset! + + FakeFS.with_fresh do + ex.run + end + end + + describe '.domain' do + context 'when /etc/login.gov exists (in a datacenter environment)' do + before { FileUtils.mkdir_p('/etc/login.gov') } + + context 'when the info/domain file exists' do + before do + FileUtils.mkdir_p('/etc/login.gov/info') + File.open('/etc/login.gov/info/domain', 'w') { |f| f.puts 'identitysandbox.gov' } + end + + it 'reads the contents of the file' do + expect(Identity::Hostdata.domain).to eq('identitysandbox.gov') + end + end + + context 'when the info/domain file does not exist' do + it 'blows up' do + expect { Identity::Hostdata.domain }. + to raise_error(Identity::Hostdata::MissingConfigError) + end + end + end + + context 'when /etc/login.gov does not exist (development environment)' do + it 'is nil' do + expect(Identity::Hostdata.domain).to eq(nil) + end + end + end + + describe '.env' do + context 'when /etc/login.gov exists (in a datacenter environment)' do + before { FileUtils.mkdir_p('/etc/login.gov') } + + context 'when the info/env file exists' do + before do + FileUtils.mkdir_p('/etc/login.gov/info') + File.open('/etc/login.gov/info/env', 'w') { |f| f.puts 'staging' } + end + + it 'reads the contents of the file' do + expect(Identity::Hostdata.env).to eq('staging') + end + end + + context 'when the info/env file does not exist' do + it 'blows up' do + expect { Identity::Hostdata.env }. + to raise_error(Identity::Hostdata::MissingConfigError) + end + end + end + + context 'when /etc/login.gov does not exist (development environment)' do + it 'is nil' do + expect(Identity::Hostdata.env).to eq(nil) + end + end + end + + describe '.in_datacenter?' do + it 'is true when the /etc/login.gov directory exists' do + FileUtils.mkdir_p('/etc/login.gov') + + expect(Identity::Hostdata.in_datacenter?).to eq(true) + end + + it 'is false when the /etc/login.gov does note exist' do + expect(Identity::Hostdata.in_datacenter?).to eq(false) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..57d1382 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,19 @@ +require "bundler/setup" +require "identity/hostdata" +require "pp" +require "fakefs/safe" +require "fakefs/spec_helpers" + +RSpec.configure do |config| + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end + + config.include FakeFS::SpecHelpers, fakefs: true +end