From a590dfead0103d9727616930ad5313044dde168d Mon Sep 17 00:00:00 2001 From: Kris Watson Date: Mon, 12 Feb 2024 18:54:06 -0800 Subject: [PATCH] New development environment. * New vagrant image that works on both Mac M1 and linux amd64. * Controller assets now run locally in docker, while the vagrant image is just the container node for a more simplified environment. --- .gitignore | 9 +- .gitlab-ci.yml | 6 +- .node-version | 1 + .rubocop.yml | 6 +- .ruby-version | 1 + Dockerfile | 8 + Vagrantfile.sample | 86 ++------ app/models/concerns/le_container_domain.rb | 26 ++- app/models/concerns/nodes/consul_node.rb | 3 +- app/models/concerns/volumes/consul_volume.rb | 3 +- app/models/concerns/volumes/volume_driver.rb | 4 +- app/models/container_registry.rb | 2 +- app/models/deployment/container_domain.rb | 11 +- app/models/node.rb | 93 +++++---- app/models/product.rb | 4 +- app/models/region.rb | 6 +- app/models/setting.rb | 1 + .../aggregate_usage_service.rb | 7 +- .../validate_domain_service.rb | 23 ++- .../update_balancer_service.rb | 4 +- .../load_writable_service.rb | 3 +- .../region_migrator_service.rb | 2 +- .../clean_volume_metadata_service.rb | 3 +- bin/dev | 3 + config/environments/development.rb | 2 +- config/environments/production.rb | 2 +- config/environments/test.rb | 2 +- config/initializers/cloudportal.rb | 27 +-- config/initializers/consul.rb | 4 + config/initializers/lets_encrypt.rb | 2 +- config/initializers/sidekiq.rb | 2 +- db/seeds.rb | 159 ++------------ doc/DEV_SETUP.md | 53 +++++ docker-compose.yml | 42 ++++ envrc.sample | 43 +++- justfile | 4 +- lib/dev/powerdns/Dockerfile | 64 ++++++ lib/dev/powerdns/pdns.conf | 20 ++ lib/dev/vagrant_provision-node.sh | 137 +++---------- lib/tasks/setup_dev.rake | 194 ++++++++++++++++++ lib/tasks/test_connection.rake | 2 +- lib/test/powerdns/pdns_up.sh | 8 +- .../acme_test_container_concern.rb | 16 -- .../consul_test_container_concern.rb | 4 +- .../ingress_rules_controller_test.rb | 2 - .../ingress_rules/domains_controller_test.rb | 1 - .../stacks/load_balancers_controller_test.rb | 6 +- test/fixtures/load_balancers.yml | 2 +- test/fixtures/log_clients.yml | 2 +- test/fixtures/metric_clients.yml | 2 +- test/fixtures/nodes.yml | 2 +- test/fixtures/provision_drivers.yml | 6 +- test/fixtures/regions.yml | 2 +- test/integration/lets_encrypt_flow_test.rb | 2 - .../service_provision_flow_test.rb | 9 +- .../service_usage/aggregate_usage_test.rb | 31 +-- .../service_usage/collect_usage_test.rb | 15 +- .../deployment/container_domain_test.rb | 2 - test/models/lets_encrypt_account_test.rb | 2 - test/models/load_balancer_test.rb | 2 - yarn.lock | 4 + 61 files changed, 686 insertions(+), 508 deletions(-) create mode 100644 .node-version create mode 100644 .ruby-version create mode 100755 bin/dev create mode 100644 doc/DEV_SETUP.md create mode 100644 docker-compose.yml create mode 100644 lib/dev/powerdns/Dockerfile create mode 100644 lib/dev/powerdns/pdns.conf create mode 100644 lib/tasks/setup_dev.rake delete mode 100644 lib/test/system_containers/acme_test_container_concern.rb create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore index 2da505d..66379fa 100644 --- a/.gitignore +++ b/.gitignore @@ -27,11 +27,6 @@ public/tmp /tmp .idea/ -# Vagrant -#.vagrant/ - -#vendor/ - # Redis dump.rdb @@ -54,7 +49,6 @@ lib/dev/ssl # Dont commit generated changelog CHANGELOG.html -.ruby-version .editorconfig node_modules yarn-error.log @@ -87,5 +81,4 @@ config/routes/engines.rb engines/** !engines/.keep -# Ignore Docker Compose for now... -docker-compose.yml +.consul.token diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 190c1a6..119a1dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,14 +13,12 @@ workflow: # CS_MAJOR_VERSION variables: FULL_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA-$CI_PIPELINE_ID - DH_IMAGE: cmptstks/controller default: tags: - shell before_script: - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY --username $CI_REGISTRY_USER --password-stdin - - echo "$DH_PW" | docker login -u $DH_USER --password-stdin after_script: - docker logout $CI_REGISTRY - docker logout @@ -32,8 +30,8 @@ stages: build: stage: build before_script: - - 'docker run -d --rm --name cstacks-pg -e POSTGRES_USER=cstacks -e POSTGRES_DB=cstacks -e POSTGRES_PASSWORD=cstacks postgres:13-alpine' - - 'docker run -d --rm --name cstacks-redis --network "container:cstacks-pg" redis:latest' + - 'docker run -d --rm --name cstacks-pg -e POSTGRES_USER=cstacks -e POSTGRES_DB=cstacks -e POSTGRES_PASSWORD=cstacks postgres:16' + - 'docker run -d --rm --name cstacks-redis --network "container:cstacks-pg" redis:alpine' after_script: - docker stop cstacks-redis cstacks-pg script: diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..8b0beab --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +20.11.0 diff --git a/.rubocop.yml b/.rubocop.yml index a13679c..e5cd65c 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -9,4 +9,8 @@ Style/StringLiterals: Layout/TrailingWhitespace: Enabled: false Style/RescueStandardError: - Enabled: false \ No newline at end of file + Enabled: false +Layout/SpaceInsideParens: + Enabled: false +Layout/SpaceInsideArrayLiteralBrackets: + Enabled: false diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..b347b11 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.3 diff --git a/Dockerfile b/Dockerfile index 85d2b73..9d7335d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -33,6 +33,14 @@ RUN set -ex; \ git \ supervisor \ nginx-light \ + libjemalloc2 \ + patchelf \ + ; \ + # Patch Ruby to use jemalloc + patchelf --add-needed libjemalloc.so.2 /usr/local/bin/ruby; \ + # Discard patchelf after use + apt-get purge -y \ + patchelf \ ; \ echo "set_real_ip_from 127.0.0.1;" > /etc/nginx/conf.d/restore_ip.conf \ ; \ diff --git a/Vagrantfile.sample b/Vagrantfile.sample index c69f728..e7a863b 100644 --- a/Vagrantfile.sample +++ b/Vagrantfile.sample @@ -1,83 +1,27 @@ # -*- mode: ruby -*- # vi: set ft=ruby : -## -# ComputeStacks Vagrantfile -# 1. Copy `envrc.sample` to `.envrc` and update values appropriately. -# * Take note that the `GITHUB_GEM_PULL_TOKEN` is a personal access token with the `read_packages` permission. -# 2. Copy `config/database.sample.yml` to `config/database.yml`. -# * _No changes are required._ -# 2. Copy `Vagrantfile.sample` to `Vagrantfile` and make any relevant changes, however it should work out of the box without any modification. -# 3. Bring up with `vagrant up`. This will take a few minutes depending on your computer -- compiling ruby takes a bit. -# -# **Enter the vagrant VM with: `vagrant ssh`** -# -# 4. Install NodeJS & Yarn -# 1. Install nodejs with `nvm install --lts` -# 2. Install yarn `npm -g install yarn` -# 5. `cd ~/controller` and run: -# 1. `direnv allow .` -# 2. `bundle` -# * if you did not setup your `.envrc` correctly, you will get an authorization error. Our provision script will automatically take the credentials in your `.envrc` file and authenticate with Github, but please also reference this document if you run into issues: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-rubygems-registry -# -# 6. Once all the gems have been installed, bootstrap your database with: -# 1. `bundle exec rails db:create` -# 2. `bundle exec rails db:schema:load` -# 3. `bundle exec rails db:seed` -# -# 7. Bootstrap the controller assets (from inside `~/controller` in vagrant) -# 1. Run `yarn` -# 2. Run `bundle exec rake assets:precompile` -# -# 8. _(optional)_ Install our default CS theme. Skip if you're building your own. -# 1. `mkdir ~/controller/public/assets/custom` -# 2. `wget -q -O ~/controller/public/assets/custom/application.css https://storage.googleapis.com/cstacks/provision_assets/branding/application.css` -# 3. `wget -q -O ~/controller/public/assets/custom/application.css.map https://storage.googleapis.com/cstacks/provision_assets/branding/application.css.map` -# 4. `wget -q -O ~/controller/public/assets/custom/logo-login.png https://storage.googleapis.com/cstacks/provision_assets/branding/logo-login.png` -# -# 9. _(optional)_ Run our test suite with: `bundle exec rails test` -# -# You may now launch ComputeStacks by running `overmind s` from the ~/controller directory within the vagrant vm. -# -# Next, proceed to your browser and visit http://localhost:3005 -- the default credentials are `admin@cstacks.local` / `changeme!` -# +# Read `./doc/DEV_SETUP.md`. Vagrant.configure("2") do |config| - config.vm.synced_folder ".", "/home/vagrant/controller" + config.vm.box = "bento/ubuntu-22.04" + config.vm.synced_folder '.', '/vagrant', disabled: true - # ComputeStacks Web UI: http://localhost:3005 - config.vm.network "forwarded_port", guest: 3005, host: 3005, auto_correct: true - - # Prometheus: http://localhost:9090 - config.vm.network "forwarded_port", guest: 9090, host: 9090, auto_correct: true - - # Consul: http://localhost:8500/ui/ - config.vm.network "forwarded_port", guest: 8500, host: 8500, auto_correct: true - - # ComputeStacks Load Balancer - config.vm.network "forwarded_port", guest: 80, host: 80 - config.vm.network "forwarded_port", guest: 443, host: 443 - - config.vm.box = "bento/debian-12" - config.vm.provider "virtualbox" do |vm| - vm.name = "computestacks" + config.vm.provider :libvirt do |vm| + vm.driver = "kvm" + vm.title = "computestacks" + vm.description = "ComputeStacks Controller" vm.memory = 4096 - vm.cpus = 2 + vm.cpus = 4 + vm.cputopology sockets: "1", cores: "2", threads: "2" end - ## - # Mac M1/2 with Parallels - # - # config.vm.box = "bento/debian-12-arm64" - # config.vm.provider "parallels" do |vm| - # vm.name = "computestacks" - # vm.memory = 4096 - # vm.cpus = 2 - # vm.check_guest_tools = true - # vm.update_guest_tools = true - # end + # For use on MacOS M1 with vmware fusion player. + config.vm.provider "vmware_desktop" do |vm| + vm.vmx["memsize"] = "4096" + vm.vmx["numvcpus"] = "4" + end - config.vm.provision "file", source: "lib/test/powerdns/pdns_up.sh", destination: "/tmp/cs_pdns_up" - config.vm.provision "shell", path: "lib/dev/vagrant_provision.sh" + config.vm.provision "shell", path: "lib/dev/vagrant_provision-node.sh" end diff --git a/app/models/concerns/le_container_domain.rb b/app/models/concerns/le_container_domain.rb index cb1766a..1d1cf0f 100644 --- a/app/models/concerns/le_container_domain.rb +++ b/app/models/concerns/le_container_domain.rb @@ -32,6 +32,7 @@ module LeContainerDomain def le_active? return false if system_domain || !le_enabled || !le_ready return false if lets_encrypt.nil? + lets_encrypt.names.include?(domain) && lets_encrypt.active? end @@ -47,6 +48,7 @@ def le_require_verify? def le_dns_allowed?(value) lb = container_service.load_balancer return nil if lb.nil? + lb.ip_allowed? value rescue false @@ -58,8 +60,10 @@ def le_dns_allowed?(value) def lets_encrypt_init! return true if lets_encrypt return false unless le_ready + account = LetsEncryptAccount.find_or_create return false if account.nil? + chosen_cert = nil chosen_cert = user.lets_encrypts.selectable.first unless Setting.le_single_domain? chosen_cert = user.lets_encrypts.create!(account: account) if chosen_cert.nil? @@ -67,14 +71,14 @@ def lets_encrypt_init! SystemEvent.create!( message: "Unable to generate LetsEncrypt for domain #{domain}", data: { - domain: { - id: id, - name: domain - }, - user: { - id: user.id, - email: user.email - } + domain: { + id: id, + name: domain + }, + user: { + id: user.id, + email: user.email + } }, event_code: "26ed782e7ccfd47b" ) @@ -84,9 +88,9 @@ def lets_encrypt_init! end def validate_le_domain - if saved_change_to_attribute?("domain") && !skip_validation - LetsEncryptWorkers::ValidateDomainWorker.perform_async(id) - end + return unless saved_change_to_attribute?("domain") && !skip_validation + + LetsEncryptWorkers::ValidateDomainWorker.perform_async(id) end def disable_le_ready diff --git a/app/models/concerns/nodes/consul_node.rb b/app/models/concerns/nodes/consul_node.rb index 598a9a2..48c8eb5 100644 --- a/app/models/concerns/nodes/consul_node.rb +++ b/app/models/concerns/nodes/consul_node.rb @@ -21,11 +21,10 @@ def update_iptable_config!(trigger_reload = true) end def consul_config - return {} if Rails.env.test? # for test, we dont want any config here! return {} unless online? dc = region.name.strip.downcase { - http_addr: Diplomat.configuration.options.empty? ? "http://#{primary_ip}:8500" : "https://#{primary_ip}:8501", + http_addr: "#{CONSUL_API_PROTO}://#{primary_ip}:#{CONSUL_API_PORT}", dc: dc.blank? ? nil : dc, token: region.consul_token } diff --git a/app/models/concerns/volumes/consul_volume.rb b/app/models/concerns/volumes/consul_volume.rb index 5c5bfa3..c0113ec 100644 --- a/app/models/concerns/volumes/consul_volume.rb +++ b/app/models/concerns/volumes/consul_volume.rb @@ -176,14 +176,13 @@ def consul_select_node end def consul_config - return {} if Rails.env.test? # for test, we dont want any config here! return {} if nodes.online.empty? dc = region.nil? ? nodes.online.first.region.name.strip.downcase : region.name.strip.downcase token = region.nil? ? nodes.online.first.region.consul_token : region.consul_token return {} if token.blank? consul_ip = nodes.online.first.primary_ip { - http_addr: Diplomat.configuration.options.empty? ? "http://#{consul_ip}:8500" : "https://#{consul_ip}:8501", + http_addr: "#{CONSUL_API_PROTO}://#{consul_ip}:#{CONSUL_API_PORT}", dc: dc.blank? ? nil : dc, token: token } diff --git a/app/models/concerns/volumes/volume_driver.rb b/app/models/concerns/volumes/volume_driver.rb index ee19cd7..22e299b 100644 --- a/app/models/concerns/volumes/volume_driver.rb +++ b/app/models/concerns/volumes/volume_driver.rb @@ -19,7 +19,7 @@ def destroy_volume! def volume_client case volume_backend when 'nfs' - DockerVolumeNfs.configure ssh_key: ENV['CS_SSH_KEY'] + DockerVolumeNfs.configure ssh_key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}" DockerVolumeNfs::Volume.new self else DockerVolumeLocal::Volume.new self @@ -29,7 +29,7 @@ def volume_client def volume_driver_client case volume_backend when 'nfs' - DockerVolumeNfs.configure ssh_key: ENV['CS_SSH_KEY'] + DockerVolumeNfs.configure ssh_key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}" DockerVolumeNfs else DockerVolumeLocal diff --git a/app/models/container_registry.rb b/app/models/container_registry.rb index 9fb1bce..76e86d5 100644 --- a/app/models/container_registry.rb +++ b/app/models/container_registry.rb @@ -238,7 +238,7 @@ def docker_client :port_map => ports, :restart_policy => "always" } - params = {image_url: "cmptstks/registry:latest", settings: options, node: {key: ENV['CS_SSH_KEY']}} + params = {image_url: "cmptstks/registry:latest", settings: options, node: {key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}"}} ssh_port = Setting.registry_ssh_port DockerSSH::Container.new(self.name, "ssh://#{Setting.registry_node}:#{ssh_port}", params) rescue => e diff --git a/app/models/deployment/container_domain.rb b/app/models/deployment/container_domain.rb index 1ab0c59..9f0ca0c 100644 --- a/app/models/deployment/container_domain.rb +++ b/app/models/deployment/container_domain.rb @@ -71,6 +71,7 @@ def csrn def resource_name return "null" if domain.blank? + domain.strip.downcase.gsub(".","-")[0..10] end @@ -82,6 +83,7 @@ def expected_dns_entries # expected to be an array pub = [] return pub if container_service&.load_balancer.nil? + pub << container_service.load_balancer.public_ip container_service.load_balancer.ipaddrs.where(role: 'public').each do |i| addr = i.ip_addr.to_s @@ -98,9 +100,11 @@ def self.create_system_domain!(service) sysd = Deployment::ContainerDomain.sys_domain(service.region).first return [] if sysd.nil? return [] if service.nil? || service.user.nil? + domains = [] service.ingress_rules.where(external_access: true).each do |ingress| next if ingress.container_domains.where(system_domain: true).exists? + domains << ingress.container_domains.create!( domain: "#{service.name}#{ingress.id}.#{sysd}", system_domain: true, @@ -117,6 +121,7 @@ def self.sys_domain(region = nil) LoadBalancer.all.map { |i| i.domain unless i.domain.blank? } else return [] if region.load_balancer.nil? + [region.load_balancer.domain] end end @@ -125,16 +130,15 @@ def self.sys_domain(region = nil) def reload_load_balancer! return true unless ingress_rule + if ingress_rule.external_access if ingress_rule.load_balancer_rule # internal load balancer # @type [Deployment::ContainerService] lb_service = ingress_rule.load_balancer_rule&.container_service - if lb_service - lb_service.containers.each do |container| + lb_service&.containers&.each do |container| PowerCycleContainerService.new(container, 'restart', current_audit).perform end - end elsif container_service&.load_balancer LoadBalancerServices::DeployConfigService.new(container_service.load_balancer).perform end @@ -169,6 +173,7 @@ def ensure_ingress_present def set_primary_domain return unless make_primary + container_service.update master_domain_id: id end diff --git a/app/models/node.rb b/app/models/node.rb index 451ba0f..7638948 100644 --- a/app/models/node.rb +++ b/app/models/node.rb @@ -17,7 +17,7 @@ # Able to connect, and existing deployments can use it. scope :online, -> { where(disconnected: false, maintenance: false) } - scope :offline, -> { where( Arel.sql( %q(disconnected = true OR maintenance = true) ) ) } + scope :offline, -> { where( Arel.sql( 'disconnected = true OR maintenance = true' ) ) } # Available for new orders scope :available, -> { where(active: true, disconnected: false, maintenance: false) } @@ -59,7 +59,7 @@ def container_count end def self.system_containers - %w( + %w[ alertmanager cadvisor calico-node @@ -68,51 +68,62 @@ def self.system_containers loki prometheus vault-bootstrap - ) + ] end def ingress_rules - container_services.map { |i| i.ingress_rules } + sftp_containers.map { |i| i.ingress_rules } + container_services.map(&:ingress_rules) + sftp_containers.map(&:ingress_rules) end # @return [Hash] def container_io_limits return {} if volume_device.blank? - return {} if (block_write_bps.zero? && block_read_bps.zero?) + return {} if block_write_bps.zero? && block_read_bps.zero? + h = {} - h['BlkioDeviceWriteBps'] = [ - { - 'Path' => volume_device, - 'Rate' => block_write_bps - } - ] unless block_write_bps.zero? - h['BlkioDeviceReadBps'] = [ - { - 'Path' => volume_device, - 'Rate' => block_read_bps - } - ] unless block_read_bps.zero? - h['BlkioDeviceWriteIOps'] = [ - { - 'Path' => volume_device, - 'Rate' => block_write_iops - } - ] unless block_write_iops.zero? - h['BlkioDeviceReadIOps'] = [ - { - 'Path' => volume_device, - 'Rate' => block_read_iops - } - ] unless block_read_iops.zero? + unless block_write_bps.zero? + h['BlkioDeviceWriteBps'] = [ + { + 'Path' => volume_device, + 'Rate' => block_write_bps + } + ] + end + unless block_read_bps.zero? + h['BlkioDeviceReadBps'] = [ + { + 'Path' => volume_device, + 'Rate' => block_read_bps + } + ] + end + unless block_write_iops.zero? + h['BlkioDeviceWriteIOps'] = [ + { + 'Path' => volume_device, + 'Rate' => block_write_iops + } + ] + end + unless block_read_iops.zero? + h['BlkioDeviceReadIOps'] = [ + { + 'Path' => volume_device, + 'Rate' => block_read_iops + } + ] + end h['OomKillDisable'] = true if region.disable_oom h['PidsLimit'] = region.pid_limit unless region.pid_limit.zero? - h['Ulimits'] = [ - { - 'Name' => 'nofile', - 'Soft' => region.ulimit_nofile_soft, - 'Hard' => region.ulimit_nofile_hard - } - ] unless region.ulimit_nofile_soft.zero? || region.ulimit_nofile_hard.zero? + unless region.ulimit_nofile_soft.zero? || region.ulimit_nofile_hard.zero? + h['Ulimits'] = [ + { + 'Name' => 'nofile', + 'Soft' => region.ulimit_nofile_soft, + 'Hard' => region.ulimit_nofile_hard + } + ] + end h end @@ -123,7 +134,7 @@ def client(timeout = 1) opts[:connect_timeout] = 15 * timeout opts[:read_timeout] = 60 * timeout opts[:write_timeout] = 60 * timeout - Docker::Connection.new("tcp://#{self.primary_ip}:2376", opts) + Docker::Connection.new("tcp://#{primary_ip}:2376", opts) end def fast_client @@ -131,11 +142,11 @@ def fast_client opts[:connect_timeout] = 5 opts[:read_timeout] = 5 opts[:write_timeout] = 5 - Docker::Connection.new("tcp://#{self.primary_ip}:2376", opts) + Docker::Connection.new("tcp://#{primary_ip}:2376", opts) end def host_client - DockerSSH::Node.new("ssh://#{self.primary_ip}:#{ssh_port}", {key: ENV['CS_SSH_KEY']}) + DockerSSH::Node.new("ssh://#{primary_ip}:#{ssh_port}", {key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}"}) end def list_all_containers @@ -146,7 +157,7 @@ def list_all_containers def iptable_rules q = "external_access = true AND port_nat > 0 AND (proto = 'udp' OR (proto = 'tcp' AND tcp_lb = false))" - (container_services.map { |i| i.ingress_rules.where( Arel.sql q ) } + sftp_containers.map { |i| i.ingress_rules.where( Arel.sql q ) }).flatten + (container_services.map { |i| i.ingress_rules.where( Arel.sql(q) ) } + sftp_containers.map { |i| i.ingress_rules.where( Arel.sql(q) ) }).flatten end private @@ -155,6 +166,7 @@ def iptable_rules # When using clustered storage, we need to ensure that our volumes exist for faster fail over. def sync_volumes return true unless region.has_clustered_storage? + region.volumes.active.each do |vol| unless vol.nodes.include? self vol.nodes << self @@ -165,6 +177,7 @@ def sync_volumes def valid_volume_path return if volume_device.blank? + if volume_device.count('/').zero? errors.add(:volume_device, 'is not a valid path') end diff --git a/app/models/product.rb b/app/models/product.rb index 5582f5e..ffbd070 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -204,11 +204,11 @@ def set_defaults end def set_aggregation - self.is_aggregated = self.resource_kind == 'bandwidth' + self.is_aggregated = resource_kind == 'bandwidth' end def update_product_name - self.name = self.label.parameterize + self.name = label.parameterize end end diff --git a/app/models/region.rb b/app/models/region.rb index 4c36917..631d781 100644 --- a/app/models/region.rb +++ b/app/models/region.rb @@ -89,7 +89,7 @@ def has_clustered_networking? def volume_driver case volume_backend when 'nfs' - DockerVolumeNfs.configure ssh_key: ENV['CS_SSH_KEY'] + DockerVolumeNfs.configure ssh_key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}" DockerVolumeNfs else DockerVolumeLocal @@ -97,12 +97,12 @@ def volume_driver end def consul_config - return {} if Rails.env.test? # for test, we dont want any config here! return {} if nodes.online.empty? + primary_ip = nodes.online.first&.primary_ip dc = name.strip.downcase { - http_addr: Diplomat.configuration.options.empty? ? "http://#{primary_ip}:8500" : "https://#{primary_ip}:8501", + http_addr: "#{CONSUL_API_PROTO}://#{primary_ip}:#{CONSUL_API_PORT}", dc: dc.blank? ? nil : dc, token: consul_token } diff --git a/app/models/setting.rb b/app/models/setting.rb index a3669d0..6427115 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -703,6 +703,7 @@ def setup! branding_img_login branding_email_logo company_name + computestacks_bastion_image computestacks_cr_le enable_signup_form? general_support_line diff --git a/app/services/billing_usage_services/aggregate_usage_service.rb b/app/services/billing_usage_services/aggregate_usage_service.rb index 2c6769d..e0e7430 100644 --- a/app/services/billing_usage_services/aggregate_usage_service.rb +++ b/app/services/billing_usage_services/aggregate_usage_service.rb @@ -47,6 +47,7 @@ def aggregate! BillingUsage.unscoped.select("distinct on (subscription_product_id) id, subscription_product_id, processed").where(processed: false).each do |i| aggreg_total = BillingUsage.unscoped.select("SUM(total) as bill_total, SUM(qty) as bill_qty").find_by(subscription_product_id: i.subscription_product_id, processed: false) next if aggreg_total.nil? || aggreg_total.bill_total < 0.01 # 1 Cent is the smallest unit we will process. + pstart = nil pend = nil ext_id = nil @@ -56,6 +57,7 @@ def aggregate! pstart = usage.period_start if pstart.nil? || usage.period_start < pstart pend = usage.period_end if pend.nil? || usage.period_end > pend next if pstart.nil? || pend.nil? + ext_id = usage.external_id if ext_id.nil? && !usage.external_id.nil? processed_records << usage items << { @@ -70,11 +72,14 @@ def aggregate! end sub_product = i.subscription_product next if sub_product.nil? + sub = sub_product.subscription next if sub.nil? + product = sub_product.product next if product.nil? next if sub_product.billing_resource.nil? || sub_product.user.nil? + aggregated << { subscription_id: sub.id, subscription_product_id: i.subscription_product_id, @@ -104,7 +109,7 @@ def aggregate! event.event_details.create!(data: aggregated.to_json, event_code: 'af5dbfa43bebd5f5') event.done! end - self.result = aggregated.map { |i| i.with_indifferent_access } + self.result = aggregated.map(&:with_indifferent_access) # Even if it does not succeed, you still need to mark them as processed because the `process_usage!()` function does not # check if data already exists before processing it; you can't run it more than once. billing_users.each do |u| diff --git a/app/services/lets_encrypt_services/validate_domain_service.rb b/app/services/lets_encrypt_services/validate_domain_service.rb index 098a607..f0448ad 100644 --- a/app/services/lets_encrypt_services/validate_domain_service.rb +++ b/app/services/lets_encrypt_services/validate_domain_service.rb @@ -64,7 +64,11 @@ def perform def valid_http? return true if Rails.env.test? # No good way to test this right now... return true if load_balancer # Not performing this check on load balancer domains right now. - response = HTTParty.get("https://#{domain}/.well-known/acme-challenge/http_check", verify: false, follow_redirects: false) + + response = HTTParty.get( + "https://#{domain}/.well-known/acme-challenge/http_check", + verify: false, follow_redirects: false + ) unless response.code == 200 event.event_details.create!( data: "Invalid domain configuration: Unable to connect to validation url. Please ensure there are no redirect rules on '/.well-known/acme-challenge/' URLs.", @@ -96,12 +100,16 @@ def valid_ip? is_valid = true (dns_resource + dns_resource_six).each do |resource| break unless is_valid + is_valid = if load_balancer - load_balancer.ip_allowed? resource - else - container_domain.le_dns_allowed? resource + load_balancer.ip_allowed? resource + else + container_domain.le_dns_allowed? resource + end + unless is_valid + event.event_details.create!(data: "Invalid DNS resource found: #{resource}", + event_code: '510806d5621c97d5') end - event.event_details.create!(data: "Invalid DNS resource found: #{resource}", event_code: '510806d5621c97d5') unless is_valid end if load_balancer @@ -146,7 +154,8 @@ def valid_caa? end if root_tld.nil? # We should always have a valid TLD! - event.event_details.create!( data: "Invalid TLD: #{full_domain}.", event_code: '1a6505df079a9b3c' ) + event.event_details.create!( data: "Invalid TLD: #{full_domain}.", + event_code: '1a6505df079a9b3c' ) return false end @@ -155,6 +164,7 @@ def valid_caa? domains = [] full_domain.gsub(".#{root_tld}",'').split('.').each do |i| break if full_domain == root_tld # Stop when we hit the root domain. + domains << full_domain full_domain = full_domain.split("#{i}.")[1] # Remove subdomain from `full_domain` and continue loop. end @@ -192,6 +202,7 @@ def valid_caa? next end next if response.empty? + invalid_records = [] invalid_wild_records = [] le_caa_exists = false diff --git a/app/services/load_balancer_services/update_balancer_service.rb b/app/services/load_balancer_services/update_balancer_service.rb index 3842de5..5ae30f4 100644 --- a/app/services/load_balancer_services/update_balancer_service.rb +++ b/app/services/load_balancer_services/update_balancer_service.rb @@ -45,7 +45,7 @@ def deploy_certificates! is_node = Node.find_by("public_ip = ? OR primary_ip = ? OR hostname = ?", i, i, i) next if is_node && !is_node.online? ssh_port = is_node ? is_node.ssh_port : 22 - client = DockerSSH::Node.new("ssh://#{i}:#{ssh_port}", { key: ENV['CS_SSH_KEY'] }) + client = DockerSSH::Node.new("ssh://#{i}:#{ssh_port}", { key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}" }) # Cleanup unused certificates begin current_certs = client.client.exec!("ls #{lb.ext_cert_dir}") @@ -120,7 +120,7 @@ def deploy_config! cmd = %Q(bash -c 'if [ ! -f #{lb.ext_dir}/default.http ]; then curl --insecure -H "Authorization: Bearer #{auth_token}" #{PORTAL_HTTP_SCHEME}://#{Setting.hostname}/api/stacks/load_balancers/assets/default > #{lb.ext_dir}/default.http; fi;' && curl --insecure -H "Authorization: Bearer #{auth_token}" #{PORTAL_HTTP_SCHEME}://#{Setting.hostname}/api/stacks/load_balancers > #{lb.ext_config} && #{lb.ext_reload_cmd}) ssh_port = is_node ? is_node.ssh_port : 22 begin - DockerSSH::Node.new("ssh://#{i}:#{ssh_port}", { key: ENV['CS_SSH_KEY'] }).client.exec!(cmd) + DockerSSH::Node.new("ssh://#{i}:#{ssh_port}", { key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}" }).client.exec!(cmd) rescue => e SystemEvent.create!( message: "Unable to provision LoadBalancer: #{lb.label}", diff --git a/app/services/metadata_services/load_writable_service.rb b/app/services/metadata_services/load_writable_service.rb index 395258a..6a524f6 100644 --- a/app/services/metadata_services/load_writable_service.rb +++ b/app/services/metadata_services/load_writable_service.rb @@ -35,14 +35,13 @@ def consul_base_path end def consul_config - return {} if Rails.env.test? # for test, we dont want any config here! return {} if @service.nodes.online.empty? dc = @service.region.nil? ? @service.nodes.online.first.region.name.strip.downcase : @service.region.name.strip.downcase token = @service.region.nil? ? @service.nodes.online.first.region.consul_token : @service.region.consul_token return {} if token.blank? consul_ip = @service.nodes.online.first.primary_ip { - http_addr: Diplomat.configuration.options.empty? ? "http://#{consul_ip}:8500" : "https://#{consul_ip}:8501", + http_addr: "#{CONSUL_API_PROTO}://#{consul_ip}:#{CONSUL_API_PORT}", dc: dc.blank? ? nil : dc, token: token } diff --git a/app/services/region_services/region_migrator_service.rb b/app/services/region_services/region_migrator_service.rb index 83fcfb1..8725cae 100644 --- a/app/services/region_services/region_migrator_service.rb +++ b/app/services/region_services/region_migrator_service.rb @@ -130,7 +130,7 @@ def infra_online? n = new_region.nodes.online.first.primary_ip begin Diplomat::Node.get_all({ - http_addr: Diplomat.configuration.options.empty? ? "http://#{n}:8500" : "https://#{n}:8501", + http_addr: "#{CONSUL_API_PROTO}://#{n}:#{CONSUL_API_PORT}", dc: dc.blank? ? nil : dc, token: new_region.consul_token }) diff --git a/app/services/volume_services/clean_volume_metadata_service.rb b/app/services/volume_services/clean_volume_metadata_service.rb index cba9b02..2cccd6e 100644 --- a/app/services/volume_services/clean_volume_metadata_service.rb +++ b/app/services/volume_services/clean_volume_metadata_service.rb @@ -45,12 +45,11 @@ def perform # @param [Region] region def consul_config(region) - return {} if Rails.env.test? # for test, we dont want any config here! return {} if region.nodes.online.empty? return {} if region.nodes.online.empty? consul_ip = region.nodes.online.first.primary_ip { - http_addr: Diplomat.configuration.options.empty? ? "http://#{consul_ip}:8500" : "https://#{consul_ip}:8501", + http_addr: "#{CONSUL_API_PROTO}://#{consul_ip}:#{CONSUL_API_PORT}", dc: region.name.strip.downcase, token: region.consul_token } diff --git a/bin/dev b/bin/dev new file mode 100755 index 0000000..0589ec7 --- /dev/null +++ b/bin/dev @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +exec overmind start -f Procfile diff --git a/config/environments/development.rb b/config/environments/development.rb index 29aee5e..f2b7a24 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -42,7 +42,7 @@ # config.cache_store = :null_store # config.cache_store = :redis_store, "redis://127.0.0.1:6379/5/cache", { expires_in: 12.hours } config.cache_store = :redis_cache_store, { - url: "redis://#{ENV['REDIS_HOST']}:6379/0", + url: "redis://#{ENV['REDIS_HOST'].blank? ? 'localhost' : ENV['REDIS_HOST']}:#{ENV['REDIS_PORT'].blank? ? '6379' : ENV['REDIS_PORT']}/0", expires_in: 6.hours } end diff --git a/config/environments/production.rb b/config/environments/production.rb index a9717c1..f330aae 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -93,7 +93,7 @@ ## Customizations # config.cache_store = :redis_store, "redis://#{ENV['REDIS_HOST']}:6379/0/cache", { expires_in: 12.hours } config.cache_store = :redis_cache_store, { - url: "redis://#{ENV['REDIS_HOST']}:6379/0", + url: "redis://#{ENV['REDIS_HOST'].blank? ? 'localhost' : ENV['REDIS_HOST']}:#{ENV['REDIS_PORT'].blank? ? '6379' : ENV['REDIS_PORT']}/0", expires_in: 12.hours } end diff --git a/config/environments/test.rb b/config/environments/test.rb index b39576d..39ceb9e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -57,7 +57,7 @@ # config.action_view.raise_on_missing_translations = true # config.cache_store = :redis_store, "redis://127.0.0.1:6379/9/cache", { expires_in: 1.minute } config.cache_store = :redis_cache_store, { - url: "redis://127.0.0.1:6379/7", + url: "redis://#{ENV['REDIS_HOST'].blank? ? 'localhost' : ENV['REDIS_HOST']}:#{ENV['REDIS_PORT'].blank? ? '6379' : ENV['REDIS_PORT']}/7", expires_in: 10.minutes } config.action_mailer.default_url_options = { host: "127.0.0.1" } diff --git a/config/initializers/cloudportal.rb b/config/initializers/cloudportal.rb index cd896cd..9d5a0d8 100644 --- a/config/initializers/cloudportal.rb +++ b/config/initializers/cloudportal.rb @@ -3,42 +3,37 @@ Monetize.assume_from_symbol = true DEFAULT_CURRENCY = Money::Currency.new(ENV['CURRENCY'].nil? ? 'USD' : ENV['CURRENCY']).symbol -begin - COMPUTESTACKS_VERSION = File.read("#{Rails.root.to_s}/VERSION").strip -rescue - COMPUTESTACKS_VERSION = "0" -end +COMPUTESTACKS_VERSION = File.read("#{Rails.root}/VERSION").strip.freeze # Load some defaults for test/dev unless Rails.env.production? LB_DEFAULT_CERT_PATH = if `whoami`.strip == 'vagrant' '/home/vagrant/.ssl_wildcard/sharedcert.pem' # This is created with vagrant provision. else - "#{Rails.root.to_s}/lib/dev/test_wildcard_ssl/sharedcert-test-crt" + "#{Rails.root}/lib/dev/test_wildcard_ssl/sharedcert-test-crt" end end - -NS_LIST = if Rails.env.production? - %w(8.8.8.8 1.1.1.1 8.8.4.4 64.6.64.6 208.67.222.222 1.0.0.1) +NS_LIST = if ENV['RESOLVER_LIST'].blank? + %w[8.8.8.8 1.1.1.1 8.8.4.4 64.6.64.6 208.67.222.222 1.0.0.1] else - ['127.0.0.1'] + ENV['RESOLVER_LIST'].split(',') end -NS_PORT = Rails.env.production? ? 53 : 5353 +NS_PORT = ENV['RESOLVER_PORT'].blank? ? 53 : ENV['RESOLVER_PORT'].to_i PORTAL_HTTP_SCHEME = Rails.env.production? ? "https" : "http" begin # Since users can set this during install, lets try to guess some possible commons words they may use to identify their installation. # We really want a unique name, and if they're just testing with 'dev' or 'demo', we want to generate a unique name. - app_id_exclude = %w(default dev development local localhost demo trial test tester testing cs cstacks cmptstks computestacks docker containers poc) + app_id_exclude = %w[default dev development local localhost demo trial test tester testing cs cstacks cmptstks computestacks docker containers poc] CS_APP_ID = if ENV['APP_ID'].blank? || app_id_exclude.include?(ENV['APP_ID']) Digest::SHA256.hexdigest(ENV['SECRET_KEY_BASE'])[-8...-1] else ENV['APP_ID'] end rescue - CS_APP_ID = "" + CS_APP_ID = "".freeze end BYTE_TO_GB = Numeric::GIGABYTE.to_f # old: 1.074e+9 # 1024, not 1000 @@ -46,9 +41,9 @@ I18n.default_locale = ENV['LOCALE'].nil? ? 'en' : ENV['LOCALE'] -DockerVolumeLocal.configure ssh_key: ENV['CS_SSH_KEY'].nil? ? "~/.ssh/id_rsa" : ENV['CS_SSH_KEY'] +DockerVolumeLocal.configure ssh_key: ENV['CS_SSH_KEY'].nil? ? "~/.ssh/id_rsa" : "#{Rails.root}/#{ENV['CS_SSH_KEY']}" -SYSTEM_CONTAINER_NAMES = %w( +SYSTEM_CONTAINER_NAMES = %w[ alertmanager cadvisor calico-node @@ -61,4 +56,4 @@ prometheus prom-nginx vault-bootstrap -) +].freeze diff --git a/config/initializers/consul.rb b/config/initializers/consul.rb index 9c91f77..908fbb9 100644 --- a/config/initializers/consul.rb +++ b/config/initializers/consul.rb @@ -1,3 +1,4 @@ +CONSUL_API_PROTO = 'http' if Rails.env.production? && Pathname.new("/root/.consul/cert.pem").exist? client_cert = OpenSSL::X509::Certificate.new(File.read('/root/.consul/cert.pem')) client_key = OpenSSL::PKey.read(File.read('/root/.consul/key.pem')) @@ -11,6 +12,7 @@ } } end + CONSUL_API_PROTO = 'https'.freeze elsif !Rails.env.production? && Pathname.new("lib/dev/ssl/consul/ca.pem").exist? client_cert = OpenSSL::X509::Certificate.new(File.read('lib/dev/ssl/consul/cert.pem')) client_key = OpenSSL::PKey.read(File.read('lib/dev/ssl/consul/key.pem')) @@ -24,4 +26,6 @@ } } end + CONSUL_API_PROTO = 'https'.freeze end +CONSUL_API_PORT = (CONSUL_API_PROTO == 'https' ? 8501 : 8500).freeze diff --git a/config/initializers/lets_encrypt.rb b/config/initializers/lets_encrypt.rb index c47cc33..13735d1 100644 --- a/config/initializers/lets_encrypt.rb +++ b/config/initializers/lets_encrypt.rb @@ -1,7 +1,7 @@ LE_DIRECTORY = if Rails.env.production? 'https://acme-v02.api.letsencrypt.org/directory' elsif Rails.env.test? - 'https://localhost:14000/dir' + "https://#{ENV['DOCKER_IP']}:#{ENV['ACME_API_PORT']}/dir" else 'https://acme-staging-v02.api.letsencrypt.org/directory' end diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index f55daec..75b063a 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,7 +1,7 @@ Sidekiq.strict_args! sidekiq_redis_conf = { - url: Rails.env.production? ? ENV["REDIS_URL"] : "redis://localhost:6379/#{Rails.env.test? ? '8' : '6'}", + url: "redis://#{ENV['REDIS_HOST'].blank? ? 'localhost' : ENV['REDIS_HOST']}:#{ENV['REDIS_PORT'].blank? ? '6379' : ENV['REDIS_PORT']}/#{Rails.env.test? ? '8' : '6'}", network_timeout: 3 } diff --git a/db/seeds.rb b/db/seeds.rb index 43f468f..02d769b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,19 +1,3 @@ -vagrant_vm_ip = if RUBY_PLATFORM =~ /linux/ # Anything else and we're running local, so just use 127.0.0.1 - `ip -j addr show dev eth0 | jq -r '.[0].addr_info | map(select(.family == "inet"))[0].local'` - else - '127.0.0.1' - end -lb_cert_path = if `whoami`.strip == 'vagrant' - '/home/vagrant/.ssl_wildcard/sharedcert.pem' - else - "#{Rails.root.to_s}/lib/dev/test_wildcard_ssl/sharedcert-test-crt" - end -puts "Creating default location and region..." -location = Location.create! name: "dev" - -consul_token = File.exist?("/home/vagrant/consul.token") ? File.read("/home/vagrant/consul.token").gsub("\n","") : "" - -region = location.regions.create! name: "dev", pid_limit: 150, ulimit_nofile_soft: 1024, ulimit_nofile_hard: 1024, consul_token: consul_token, network_driver: 'bridge' puts "Creating system block content..." puts "...Collaborate: Warning..." @@ -108,7 +92,7 @@ puts "Creating container image providers..." puts "Creating DockerHub Image Provider..." -dh_provider = ContainerImageProvider.create!( +ContainerImageProvider.create!( name: "DockerHub", is_default: true, hostname: "" @@ -138,38 +122,24 @@ hostname: "ghcr.io" ) -puts "Creating Container Images..." -Rake::Task['load_containers'].execute - puts "Setting up billing products..." - plan = BillingPlan.create! name: 'default', is_default: true # Container Package: Small -container_product = Product.create! name: 'small', label: 'Small', kind: 'package' -container_product.create_package cpu: 1, memory: 512, storage: 10, local_disk: 5, bandwidth: 500 -container_product_resource = plan.billing_resources.create! product: container_product -container_product_resource.prices.create! price: 0.00343, - currency: 'USD', - billing_phase: container_product_resource.billing_phases.first, - regions: Region.all +puts "..small container product" +Product.create! label: 'Small', kind: 'package' # Storage Product -storage_product = Product.create! name: 'storage', - label: 'Storage', - kind: 'resource', - unit: 1, - unit_type: 'GB', - resource_kind: 'storage', - is_aggregated: false # billed per hour/month, per GB -storage_resource = plan.billing_resources.create! product: storage_product -storage_resource.prices.create! price: 0.0001369863, - currency: 'USD', - billing_phase: storage_resource.billing_phases.first, - regions: Region.all +Product.create! label: 'Storage', + kind: 'resource', + unit: 1, + unit_type: 'GB', + resource_kind: 'storage', + is_aggregated: false # billed per hour/month, per GB + # Local Disk -local_disk_product = Product.create!( - name: 'local_disk', +puts "...local disk product" +Product.create!( label: 'Temporary Disk', kind: 'resource', unit: 1, @@ -177,15 +147,10 @@ resource_kind: 'local_disk', is_aggregated: false ) -local_disk_resource = plan.billing_resources.create! product: local_disk_product -local_disk_resource.prices.create! price: 0.0001369863, - currency: 'USD', - billing_phase: local_disk_resource.billing_phases.first, - regions: Region.all # Bandwidth Product -bandwidth_product = Product.create!( - name: 'bandwidth', +puts "...local bandwidth product" +Product.create!( label: 'Bandwidth', kind: 'resource', unit: 1, @@ -193,14 +158,10 @@ resource_kind: 'bandwidth', is_aggregated: true # Once you used it, you pay for it. ) -bandwidth_resource = plan.billing_resources.create!(product: bandwidth_product) -bandwidth_resource.prices.create!(price: 0, max_qty: 1024, currency: 'USD', billing_phase: bandwidth_resource.billing_phases.first, regions: Region.all) # First 1TB is free -bandwidth_resource.prices.create!(price: 0.09, max_qty: 10240, currency: 'USD', billing_phase: bandwidth_resource.billing_phases.first, regions: Region.all) # 1TB - 10TB -bandwidth_resource.prices.create!(price: 0.07, max_qty: nil, currency: 'USD', billing_phase: bandwidth_resource.billing_phases.first, regions: Region.all) # 10TB+ # Backup Product -backup_product = Product.create!( - name: 'backup', +puts "...local backup product" +Product.create!( label: 'Backup & Template Storage', kind: 'resource', unit: 1, @@ -208,100 +169,16 @@ resource_kind: 'backup', is_aggregated: false ) -backup_resource = plan.billing_resources.create!(product: backup_product) -backup_resource.prices.create!(price: 0.0000684932, currency: 'USD', billing_phase: backup_resource.billing_phases.first, regions: Region.all) - -BillingResourcePrice.all.each do |i| - i.regions << Region.first -end - -UserGroup.all.each do |g| - g.update billing_plan: plan -end puts "Setting up settings & features..." Setting.setup! Feature.setup! -puts "Setting default settings to dev environment..." -Setting.find_by(name: 'hostname').update value: 'controller.cstacks.local:3005' -Setting.find_by(name: 'cr_le').update value: 'controller.cstacks.local' -Setting.find_by(name: 'registry_base_url').update value: 'registry.cstacks.local' -Setting.find_by(name: 'registry_node').update value: '127.0.0.1' -Setting.find_by(name: 'registry_ssh_port').update value: '22' -Setting.find_by(name: 'le_validation_server').update value: '127.0.0.1:3005' -Setting.find_by(name: 'cs_bastion_image').update value: 'ghcr.io/computestacks/cs-docker-bastion:latest' - -puts "Creating log & metric clients..." -mc = MetricClient.create! endpoint: 'http://127.0.0.1:9090' -lc = LogClient.create! endpoint: 'http://127.0.0.1:3100' -region.update metric_client: mc, log_client: lc, loki_endpoint: 'http://127.0.0.1:3100' - -puts "Creating dev node..." -region.nodes.create! label: 'csdev', - hostname: 'csdev', - primary_ip: vagrant_vm_ip.gsub("\n",""), - public_ip: '127.0.0.1', - active: true, - ssh_port: 22, - block_write_bps: 0, - block_read_bps: 0, - block_write_iops: 0, - block_read_iops: 0 - -puts "Creating network..." -network = Network.create! subnet: "10.167.0.0/21", - is_public: true, - is_ipv4: true, - active: true, - name: "dev", - label: "Dev Network", - network_driver: 'bridge', - region: region -network.regions << region - -puts "Configuring DNS..." -pdns_api_key = File.exist?("/home/vagrant/.pdns/api_key") ? File.read("/home/vagrant/.pdns/api_key").gsub("\n","") : "" -pdns_web_key = File.exist?("/home/vagrant/.pdns/web_auth_pass") ? File.read("/home/vagrant/.pdns/web_auth_pass").gsub("\n","") : "" - -pdns_driver = ProvisionDriver.create!( - endpoint: 'http://localhost:8081/api/v1/servers', - settings: { - config: { - zone_type: 'Native', - masters: [], # When zone_type == 'Master', add the primary NS server here. - nameservers: ['ns1.cstacks.local.'], - server: 'localhost' - } - }, - module_name: 'Pdns', - username: 'admin', - api_key: Secret.encrypt!(pdns_web_key), # WebServer PW - api_secret: Secret.encrypt!(pdns_api_key) # API-Key -) - -dns_type = ProductModule.create!(name: 'dns', primary: pdns_driver) -pdns_driver.product_modules << dns_type -Dns::Zone.create! name: 'cstacks.local', - provider_ref: 'cstacks.local.', - provision_driver: pdns_driver - -puts "Creating load balancer..." -LoadBalancer.create! label: 'dev', - region: region, - domain: 'a.cstacks.local', - ext_ip: [ '127.0.0.1' ], - internal_ip: [ '127.0.0.1', vagrant_vm_ip.gsub("\n","") ], - public_ip: '127.0.0.1', - direct_connect: true, - cert_encrypted: Secret.encrypt!(File.read(lb_cert_path)), - skip_validation: true - puts "Creating default admin user..." group = UserGroup.create! name: 'default', is_default: true, billing_plan: plan -group.regions << region + user = User.new fname: 'Default', lname: 'User', email: 'admin@cstacks.local', @@ -315,3 +192,5 @@ user.save +puts "Creating Container Images..." +Rake::Task['load_containers'].execute diff --git a/doc/DEV_SETUP.md b/doc/DEV_SETUP.md new file mode 100644 index 0000000..4232bc1 --- /dev/null +++ b/doc/DEV_SETUP.md @@ -0,0 +1,53 @@ +# Setup your Developer Environment + +Ensure your local machine has the following installed and working: + + - docker & docker compose + - For MacOS, we have tested this against [colima](https://github.com/abiosoft/colima). Make sure you start it with `--network-address`. + - Ruby 3.2 + - Using `rbenv`: `rbenv install`. (from the root of the controller directory) + - NodeJS 20-LTS + - Using `nodenv`: `nodenv install` + - Overmind process manager: [Overmind](https://github.com/DarthSim/overmind) + + +## Setup Containers and Node VM + +First, create your `Vagrantfile` by `cp Vagrantfile.sample Vagrantfile` and make the necessary changes for your environment. + + - For Linux, we recommend using `libvirt`. You'll need to have a working installation of kvm, qemu, and libvirtd. + - For Mac, we recommend vmware fusion and the vmware desktop plugin for vagrant. They offer a free non-commercial license. + +```bash +docker compose up -d +vagrant up +``` + +## Environmental Variables + +```bash +cp envrc.sample .envrc +``` + +Modify the values to match your needs. Once you're done with that, you can configure your GitHub authentication with something like: + +```bash +bundle config https://rubygems.pkg.github.com/computestacks $GITHUB_GEM_PULL_USER:$GITHUB_GEM_PULL_TOKEN +``` + +Then install all the gems with: `bundle install` + +## Bootstrap Application + +```bash +./bin/rails db:setup +./bin/rake setup_dev +``` + +After all that, you should be able to run the test suite: `./bin/rails test`. + +## Running + +Run the controller with: `./bin/dev`. + + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7e74486 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,42 @@ +version: "3.6" +services: + postgres: + image: postgres:16 + environment: + - POSTGRES_PASSWORD=controller + - POSTGRES_USER=controller + - POSTGRES_DB=controller + volumes: + - postgres:/var/lib/postgresql/data + ports: + - "${DB_PORT}:5432" + redis: + image: redis:alpine + command: redis-server --save '' --appendonly no + volumes: + - redis:/data + ports: + - "${REDIS_PORT}:6379" + powerdns: + build: ./lib/dev/powerdns + # build: + # context: ./lib/dev/powerdns + # args: + # LUA_VERSION: 5.1 + # AUTH_VERSION: 4.8.4 + command: --daemon=no --guardian=no --loglevel=9 + init: true + ports: + - "${PDNS_DNS_PORT}:53/udp" + - "${PDNS_API_PORT}:8081/tcp" + acme_test: # Only used for automated testing + image: letsencrypt/pebble:latest + command: pebble -config /test/config/pebble-config.json -strict + environment: + - GODEBUG="tls13=1" + - PEBBLE_VA_ALWAYS_VALID=1 + ports: + - "${ACME_API_PORT}:14000" +volumes: + postgres: + redis: diff --git a/envrc.sample b/envrc.sample index f816daf..12f303e 100644 --- a/envrc.sample +++ b/envrc.sample @@ -1,8 +1,43 @@ +# Linux should be `localhost` +# Using Colima on mac? `colima start --network-address && colima status -e` to get the IP. +export DOCKER_IP=localhost + +# Vagrant IP will be assigned on first startup, and should not change until you destroy the VM. +# This IP should be accessible from your local machine. +export VAGRANT_VM_IP=localhost + +# You need to authenticate with Github to access our public gem repository. +export GITHUB_GEM_PULL_USER="your github username" +export GITHUB_GEM_PULL_TOKEN="your github personal token" + +## +# You should not need to change anything else below this section. +## + export LOCALE=en export CURRENCY=USD -export CS_SSH_KEY=/home/vagrant/.ssh/id_rsa +export CS_SSH_KEY=lib/dev/keys/id_ed25519 export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES -export GITHUB_GEM_PULL_USER=changme -export GITHUB_GEM_PULL_TOKEN=changme -export RUBYOPT='-W:no-deprecated' +export INGRESS_TEST_URI=localhost/ingress export BUNDLE_GEMFILE=Gemfile.common + +# PowerDNS And ACME (used for testing). +export PDNS_DNS_PORT=55003 +export PDNS_API_PORT=55004 +export ACME_API_PORT=55005 + +# Local Resolver for testing +export RESOLVER_LIST="$DOCKER_IP" +export RESOLVER_PORT="$PDNS_DNS_PORT" + +# this will be copied from the Vagrant VM during the rake task `setup_dev`. +export CONSUL_TOKEN_PATH=".consul.token" + +# Postgres +export DB_PORT=55001 +export DEV_DB_URL=postgresql://controller:controller@$DOCKER_IP:$DB_PORT/computestacks +export TEST_DB_URL=postgresql://controller:controller@$DOCKER_IP:$DB_PORT/computestacks_test + +# Redis +export REDIS_HOST="$DOCKER_IP" +export REDIS_PORT=55002 diff --git a/justfile b/justfile index 6c2be93..89cdddf 100644 --- a/justfile +++ b/justfile @@ -16,9 +16,9 @@ build: --health-interval 10s \ --health-timeout 5s \ --health-retries 5 \ - postgres:15-alpine + postgres:16-alpine until [ "`docker inspect -f {{{{.State.Health.Status}}}} cstacks-build-pg`"=="healthy" ]; do sleep 0.1; done; - docker run -it -d --rm --network "container:cstacks-build-pg" --name cstacks-build-redis --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 redis:7 + docker run -it -d --rm --network "container:cstacks-build-pg" --name cstacks-build-redis --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 redis:alpine until [ "`docker inspect -f {{{{.State.Health.Status}}}} cstacks-build-redis`"=="healthy" ]; do sleep 0.1; done; DOCKER_BUILDKIT=0 docker build -t computestacks:dev --build-arg="github_user=$GITHUB_GEM_PULL_USER" --build-arg="github_token=$GITHUB_GEM_PULL_TOKEN" --network="container:cstacks-build-pg" . @docker stop cstacks-build-redis cstacks-build-pg diff --git a/lib/dev/powerdns/Dockerfile b/lib/dev/powerdns/Dockerfile new file mode 100644 index 0000000..5e43c5f --- /dev/null +++ b/lib/dev/powerdns/Dockerfile @@ -0,0 +1,64 @@ +FROM tcely/powerdns-server + +ADD pdns.conf /etc/pdns/pdns.conf + +RUN apk add sqlite \ + && mkdir -p /etc/pdns/sqlite-db \ + && sqlite3 /etc/pdns/sqlite-db/pdns.db < /usr/local/share/doc/pdns/schema.sqlite3.sql \ + && chown pdns:pdns -R /etc/pdns \ + && rm -rf /var/cache/apk/* \ + ; \ + pdnsutil create-zone cstacks.local ns1.cstacks.local \ + && pdnsutil add-record cstacks.local ns1 A 127.0.0.1 \ + && pdnsutil add-record cstacks.local @ A 127.0.0.1 \ + && pdnsutil add-record cstacks.local controller A 127.0.0.1 \ + && pdnsutil add-record cstacks.local registry A 127.0.0.1 \ + && pdnsutil add-record cstacks.local a A 127.0.0.1 \ + && pdnsutil add-record cstacks.local *.a CNAME a.cstacks.local \ + && pdnsutil add-record cstacks.local @ CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil create-zone csdevserver.local ns1.cstacks.local \ + && pdnsutil add-record csdevserver.local @ A 127.0.0.1 \ + && pdnsutil add-record csdevserver.local @ CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil create-zone usrcloud.local ns1.cstacks.local \ + && pdnsutil add-record usrcloud.local @ A 127.0.0.1 \ + && pdnsutil add-record usrcloud.local www CNAME usrcloud.local \ + && pdnsutil create-zone usrca.local ns1.cstacks.local \ + && pdnsutil add-record usrca.local @ A 127.0.0.1 \ + && pdnsutil add-record usrca.local www CNAME usrca.local \ + && pdnsutil add-record usrca.local @ CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil create-zone cstacksorg.local ns1.cstacks.local \ + && pdnsutil add-record cstacksorg.local @ A 127.0.0.1 \ + && pdnsutil create-zone csnet.local ns1.cstacks.local \ + && pdnsutil add-record csnet.local @ A 127.0.0.1 \ + && pdnsutil add-record csnet.local @ CAA "0 issue \"digicert.com\"" \ + && pdnsutil create-zone cstacksus.local ns1.cstacks.local \ + && pdnsutil add-record cstacksus.local test A 127.0.0.1 \ + && pdnsutil add-record cstacksus.local @ CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil add-record cstacksus.local test CAA "0 issue \"digicert.com\"" \ + && pdnsutil create-zone cstackscc.local ns1.cstacks.local \ + && pdnsutil add-record cstackscc.local test A 127.0.0.1 \ + && pdnsutil add-record cstackscc.local @ CAA "0 issue \"digicert.com\"" \ + && pdnsutil add-record cstackscc.local test CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil create-zone myshared.local ns1.cstacks.local \ + && pdnsutil add-record myshared.local app A 127.0.0.1 \ + && pdnsutil add-record myshared.local *.app CNAME app.myshared.local \ + && pdnsutil add-record myshared.local @ CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil add-record myshared.local @ CAA "0 issuewild \"letsencrypt.org\"" \ + && pdnsutil create-zone mysharedus.local ns1.cstacks.local \ + && pdnsutil add-record mysharedus.local app A 192.168.10.10 \ + && pdnsutil add-record mysharedus.local *.app CNAME app.mysharedus.local \ + && pdnsutil add-record mysharedus.local @ CAA "0 issue \"letsencrypt.org\"" \ + && pdnsutil add-record mysharedus.local @ CAA "0 issuewild \"letsencrypt.org\"" \ + && pdnsutil create-zone mysharednet.local ns1.cstacks.local \ + && pdnsutil add-record mysharednet.local app A 127.0.0.1 \ + && pdnsutil add-record mysharednet.local *.app CNAME app.mysharedus.local \ + && pdnsutil add-record mysharednet.local @ CAA "0 issue \"digicert.com\"" \ + && pdnsutil add-record mysharednet.local @ CAA "0 issuewild \"digicert.com\"" \ + && pdnsutil create-zone mysharedorg.local ns1.cstacks.local \ + && pdnsutil add-record mysharedorg.local app A 127.0.0.1 \ + && pdnsutil add-record mysharedorg.local *.app CNAME app.mysharedorg.local \ + && pdnsutil create-zone mysharedapp.local ns1.cstacks.local \ + && pdnsutil add-record mysharedapp.local app A 127.0.0.1 \ + && pdnsutil add-record mysharedapp.local *.app CNAME app.mysharedapp.local \ + && pdnsutil add-record mysharedapp.local @ CAA "0 issue \"letsencrypt.org\"" + diff --git a/lib/dev/powerdns/pdns.conf b/lib/dev/powerdns/pdns.conf new file mode 100644 index 0000000..2916457 --- /dev/null +++ b/lib/dev/powerdns/pdns.conf @@ -0,0 +1,20 @@ +master=yes +local-port=53 +local-address=0.0.0.0 +config-dir=/etc/pdns +launch=gsqlite3 +gsqlite3-database=/etc/pdns/sqlite-db/pdns.db +security-poll-suffix= +default-soa-edit=inception-increment +default-ttl=14400 +allow-axfr-ips=127.0.0.1/32 +dnsupdate=yes +allow-dnsupdate-from= +query-cache-ttl=20 +api=yes +api-key=d3vE_nv1rnm3n1 +webserver=yes +webserver-address=0.0.0.0 +webserver-port=8081 +webserver-allow-from=0.0.0.0/0 +webserver-password=d3vE_nv1rnm3n1 diff --git a/lib/dev/vagrant_provision-node.sh b/lib/dev/vagrant_provision-node.sh index 8fe45f2..a04f8af 100644 --- a/lib/dev/vagrant_provision-node.sh +++ b/lib/dev/vagrant_provision-node.sh @@ -1,13 +1,12 @@ -#!/bin/bash -# Debian 12 Bookworm +#!/usr/bin/env bash set -e +CURRENT_DISTRO=$(lsb_release -i | awk -F"\t" '{print $2}') + hostname csdev echo "csdev" > /etc/hostname echo "127.0.0.1 csdev" >> /etc/hosts -echo "127.0.0.1 controller.cstacks.local" >> /etc/hosts -echo "127.0.0.1 ns1.cstacks.local" >> /etc/hosts echo "export EDITOR=vim" >> /etc/profile echo "export EDITOR=vim" >> /root/.profile echo "export EDITOR=vim" >> /home/vagrant/.profile @@ -15,43 +14,41 @@ echo "alias dconsole='docker exec -e COLUMNS=\"\`tput cols\`\" -e LINES=\"\`tput echo "alias dconsole='docker exec -e COLUMNS=\"\`tput cols\`\" -e LINES=\"\`tput lines\`\" -it'" >> /home/vagrant/.bashrc echo "Defaults:vagrant env_keep += \"EDITOR\"" >> /etc/sudoers.d/vagrant echo "syntax on" >> /etc/vim/vimrc +apt-get -y remove ufw apt-get update && apt-get -y upgrade -apt-get -y install apt-utils software-properties-common ca-certificates curl wget lsb-release iputils-ping vim openssl dnsutils gnupg2 pass traceroute tree iptables jq whois socat git rsync apt-transport-https gnupg-agent prometheus-node-exporter git tmux pwgen +apt-get -y install apt-utils software-properties-common ca-certificates net-tools curl wget lsb-release iputils-ping vim openssl dnsutils gnupg2 pass traceroute tree iptables jq whois socat git rsync apt-transport-https gnupg-agent prometheus-node-exporter git tmux pwgen -if [[ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ]]; then - curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --batch --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg -fi +# Distro specific +if [[ "${CURRENT_DISTRO}" == 'Ubuntu' ]]; then + install -m 0755 -d /etc/apt/keyrings -echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ - $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + if [[ ! -f /etc/apt/keyrings/docker.asc ]]; then + curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc + fi + echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null -sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list' -wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - + add-apt-repository -y ppa:vbernat/haproxy-2.8 -if [ "$(dpkg --print-architecture)" == 'amd64' ]; then - install -d /etc/apt/keyrings; curl https://repo.powerdns.com/FD380FBB-pub.asc | sudo tee /etc/apt/keyrings/auth-48-pub.asc - # for arm/m1, it will fallback to the version included in the debian repository. - echo "deb [signed-by=/etc/apt/keyrings/auth-48-pub.asc arch=amd64] http://repo.powerdns.com/debian bookworm-auth-48 main" > /etc/apt/sources.list.d/pdns.list +else # Debian - cat << 'EOF' > /etc/apt/preferences.d/pdns -Package: pdns-* -Pin: origin repo.powerdns.com -Pin-Priority: 600 -EOF -else - echo "Falling back to debian repository for powerdns due to arm architecture." -fi + if [[ ! -f /usr/share/keyrings/docker-archive-keyring.gpg ]]; then + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --batch --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + fi + + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + + if [[ ! -f /usr/share/keyrings/haproxy.debian.net.gpg ]]; then + curl https://haproxy.debian.net/bernat.debian.org.gpg | gpg --batch --dearmor > /usr/share/keyrings/haproxy.debian.net.gpg + fi + + echo deb "[signed-by=/usr/share/keyrings/haproxy.debian.net.gpg]" http://haproxy.debian.net bookworm-backports-2.8 main > /etc/apt/sources.list.d/haproxy.list -if [[ ! -f /usr/share/keyrings/haproxy.debian.net.gpg ]]; then - curl https://haproxy.debian.net/bernat.debian.org.gpg | gpg --batch --dearmor > /usr/share/keyrings/haproxy.debian.net.gpg fi -echo deb "[signed-by=/usr/share/keyrings/haproxy.debian.net.gpg]" \ - http://haproxy.debian.net bookworm-backports-2.8 main \ - > /etc/apt/sources.list.d/haproxy.list +## End distro-specific apt-get update -apt-get -y install docker-ce docker-ce-cli containerd.io haproxy=2.8.\* pdns-server pdns-backend-pgsql postgresql-16 postgresql-client-16 +apt-get -y install docker-ce docker-ce-cli containerd.io haproxy=2.8.\* mkdir -p /etc/systemd/system/docker.service.d cat << 'EOF' > /etc/systemd/system/docker.service.d/startup.conf @@ -63,54 +60,6 @@ EOF systemctl daemon-reload && systemctl restart docker -systemctl stop pdns - -echo "Setting up powerdns..." -export PDNS_API_KEY=$(pwgen -s 32 1) -export PDNS_WEB_KEY=$(pwgen -s 32 1) -export PDNS_DB_PASS=$(pwgen -s 32 1) - -mkdir /home/vagrant/.pdns -echo $PDNS_API_KEY > /home/vagrant/.pdns/api_key -echo $PDNS_WEB_KEY > /home/vagrant/.pdns/web_auth_pass - -sudo -u postgres psql -c "create role pdns with superuser login password '$PDNS_DB_PASS'" -sudo -u postgres psql -c 'create database pdns owner pdns' -sudo -u postgres psql -d pdns -f /usr/share/doc/pdns-backend-pgsql/schema.pgsql.sql -rm /etc/powerdns/named.conf -rm /etc/powerdns/pdns.d/bind.conf - -cat << EOF > /etc/powerdns/pdns.d/pdns.local.gpgsql.conf -gpgsql-host=localhost -gpgsql-dbname=pdns -gpgsql-user=pdns -gpgsql-password=$PDNS_DB_PASS -gpgsql-dnssec=yes -EOF - -cat << EOF > /etc/powerdns/pdns.conf -api=yes -api-key=$PDNS_API_KEY -webserver=yes -webserver-address=0.0.0.0 -webserver-port=8081 -webserver-allow-from=0.0.0.0/0 -webserver-password=$PDNS_WEB_KEY -local-port=5353 -local-address=127.0.0.1 -include-dir=/etc/powerdns/pdns.d -launch=gpgsql -config-dir=/etc/powerdns -default-soa-edit=inception-increment -default-ttl=14400 -default-soa-content=ns1.cstacks.local hostmaster.@ 0 10800 3600 604800 3600 -query-cache-ttl=20 -dnsupdate=yes -allow-dnsupdate-from= -EOF - -systemctl enable pdns && systemctl start pdns - docker volume create consul-data mkdir -p /etc/consul cat << 'EOF' > /etc/consul/config.hcl @@ -363,8 +312,6 @@ export CONSUL_HTTP_TOKEN echo "export CONSUL_HTTP_TOKEN=$CONSUL_HTTP_TOKEN" >> /home/vagrant/.profile echo "export CONSUL_HTTP_TOKEN=$CONSUL_HTTP_TOKEN" >> /root/.profile -echo "export SECRET_KEY_BASE=$(openssl rand -hex 64)" >> /home/vagrant/.profile -echo "export USER_AUTH_SECRET=$(openssl rand -hex 64)" >> /home/vagrant/.profile echo "$CONSUL_HTTP_TOKEN" > /home/vagrant/consul.token && chown vagrant:vagrant /home/vagrant/consul.token curl -X PUT -H "X-Consul-Token: $CONSUL_HTTP_TOKEN" -H "Content-Type: application/json" --data "{\"Token\": \"$CONSUL_HTTP_TOKEN\"}" http://localhost:8500/v1/agent/token/default @@ -590,7 +537,7 @@ ExecStart=/usr/bin/env docker run --rm --name prometheus \ --label com.computestacks.role=system \ -v prometheus-data:/prometheus \ -v /etc/prometheus:/etc/prometheus:z \ - prom/prometheus:latest --storage.tsdb.path=/prometheus --storage.tsdb.retention.time=10d --storage.tsdb.wal-compression --web.console.templates=/usr/share/prometheus/consoles --web.console.libraries=/usr/share/prometheus/console_libraries --storage.tsdb.max-block-duration=1d12h + prom/prometheus:latest --config.file=/etc/prometheus/prometheus.yml --storage.tsdb.path=/prometheus --storage.tsdb.retention.time=10d --storage.tsdb.wal-compression --web.console.templates=/usr/share/prometheus/consoles --web.console.libraries=/usr/share/prometheus/console_libraries --storage.tsdb.max-block-duration=1d12h ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker kill prometheus 2>/dev/null' ExecStop=-/usr/bin/env sh -c '/usr/bin/env docker rm prometheus 2>/dev/null' @@ -889,9 +836,6 @@ systemctl daemon-reload \ && systemctl start alertmanager prometheus loki fluentd \ && systemctl enable alertmanager prometheus loki fluentd -sudo -u postgres psql -c 'create role vagrant with superuser login' -sudo -u postgres psql -c 'create database vagrant owner vagrant' - echo "Adding vagrant public key to root user" mkdir -p /root/.ssh && chmod 700 /root/.ssh cat /home/vagrant/.ssh/authorized_keys >> /root/.ssh/authorized_keys @@ -931,8 +875,8 @@ iptables -A INPUT -p icmp -j ACCEPT # For dev, open up... # ...SSH iptables -A INPUT -p tcp --dport 22 -j ACCEPT -# ...computestacks controller -iptables -A INPUT -p tcp --dport 3005 -j ACCEPT +# ...Docker API +iptables -A INPUT -p tcp --dport 2376 -j ACCEPT # ...consul UI iptables -A INPUT -p tcp --dport 8500 -j ACCEPT # ...prometheus @@ -946,9 +890,6 @@ iptables -A INPUT -p tcp --dport 443 -j ACCEPT iptables -A INPUT -p tcp --dport 10000:50000 -j ACCEPT iptables -A INPUT -p udp --dport 10000:50000 -j ACCEPT -# Allow Private Container Network Access to Host -iptables -A INPUT -s 10.134.0.0/21 -j ACCEPT - iptables -P INPUT DROP EOF @@ -956,21 +897,9 @@ EOF chmod +x /usr/local/bin/cs-recover_iptables && bash /usr/local/bin/cs-recover_iptables fi -echo "Provisioning PowerDNS for ComputeStacks..." -pdnsutil create-zone cstacks.local ns1.cstacks.local -pdnsutil add-record cstacks.local @ A 127.0.0.1 -pdnsutil add-record cstacks.local ns1 A 127.0.0.1 -pdnsutil add-record cstacks.local controller A 127.0.0.1 -pdnsutil add-record cstacks.local registry A 127.0.0.1 -pdnsutil add-record cstacks.local a A 127.0.0.1 -pdnsutil add-record cstacks.local *.a CNAME a.cstacks.local -pdnsutil add-record cstacks.local @ CAA "0 issue \"letsencrypt.org\"" - -if [ -f /tmp/cs_pdns_ip ]; then - echo "...loading test domains..." - mv /tmp/cs_pdns_up /usr/local/bin/ \ - && bash /usr/local/bin/cs_pdns_up >/dev/null -fi +echo "Adding controller ssh key..." +echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQo4kmGXgVQMOw7uKVc5mZowf0ZhAXMhqWleYBq1tJ7 controller@dev" >> /home/vagrant/.ssh/authorized_keys +echo "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGQo4kmGXgVQMOw7uKVc5mZowf0ZhAXMhqWleYBq1tJ7 controller@dev" >> /root/.ssh/authorized_keys echo "Pulling docker images..." docker pull ghcr.io/computestacks/cs-docker-bastion:latest diff --git a/lib/tasks/setup_dev.rake b/lib/tasks/setup_dev.rake new file mode 100644 index 0000000..e47b699 --- /dev/null +++ b/lib/tasks/setup_dev.rake @@ -0,0 +1,194 @@ +task setup_dev: :environment do + + vagrant_vm_ip = if ENV['USER'] == 'vagrant' # Anything else and we're running local, so just use 127.0.0.1 + `ip -j addr show dev ens18 | jq -r '.[0].addr_info | map(select(.family == "inet"))[0].local'` + else + ENV['VAGRANT_VM_IP'].blank? ? '127.0.0.1' : ENV['VAGRANT_VM_IP'] + end + vagrant_vm_ip = vagrant_vm_ip.strip + + puts "Creating default location and region..." + location = Location.where(name: 'dev').exists? ? Location.find_by(name: 'dev') : Location.create!(name: "dev") + + ssh_client = DockerSSH::Node.new("ssh://#{vagrant_vm_ip}:22", {key: "#{Rails.root}/#{ENV['CS_SSH_KEY']}"}) + + consul_token = if ENV['USER'] == 'vagrant' + File.exist?("/home/vagrant/consul.token") ? File.read("/home/vagrant/consul.token").gsub("\n","") : "" + elsif vagrant_vm_ip != '127.0.0.1' + ssh_client.client.exec!("if [[ -f /home/vagrant/consul.token ]]; then cat /home/vagrant/consul.token; else echo ''; fi").strip + else + '' + end + + if consul_token.blank? + puts "Error, unable to read consul token. Check connection settings." + return + end + + consul_token_path = ENV['CONSUL_TOKEN_PATH'].strip + + if consul_token_path != "/home/vagrant/consul.token" && !consul_token_path.blank? + File.write("#{Rails.root}/#{consul_token_path}", consul_token) + end + + lb_cert = if ENV['USER'] == 'vagrant' + File.read('/home/vagrant/.ssl_Wildcard/sharedcert.pem') + elsif vagrant_vm_ip != '127.0.0.1' + ssh_client.client.exec!("if [[ -f /home/vagrant/.ssl_wildcard/sharedcert.pem ]]; then cat /home/vagrant/.ssl_wildcard/sharedcert.pem; else echo ''; fi").strip + else + File.read("#{Rails.root}/lib/dev/test_wildcard_ssl/sharedcert-test-crt") + end + + if lb_cert.blank? + puts "Error, unable to load wildcard certificate file" + return + end + + region = if location.regions.empty? + location.regions.create!( + name: "dev", + pid_limit: 150, + ulimit_nofile_soft: 1024, + ulimit_nofile_hard: 1024, + consul_token: consul_token, + network_driver: 'bridge' + ) + else + location.regions.first + end + + if LoadBalancer.all.empty? + LoadBalancer.create! label: 'dev', + region: region, + domain: 'a.cstacks.local', + ext_ip: [ '127.0.0.1' ], + internal_ip: [ '127.0.0.1', vagrant_vm_ip ], + public_ip: '127.0.0.1', + direct_connect: true, + cert_encrypted: Secret.encrypt!(lb_cert), + skip_validation: true + end + + + # put default group into region + group = UserGroup.find_by name: 'default', is_default: true + group.regions << region unless group.regions.include?(region) + # Default Prices + plan = BillingPlan.find_by is_default: true + + container_product = Product.find_by name: 'small', kind: 'package' + if container_product.package.nil? + container_product.create_package cpu: 1, memory: 512, storage: 10, local_disk: 5, bandwidth: 500 + container_product_resource = plan.billing_resources.create! product: container_product + container_product_resource.prices.create! price: 0.00343, + currency: 'USD', + billing_phase: container_product_resource.billing_phases.first, + regions: Region.all + end + + storage_product = Product.find_by name: 'storage', kind: 'resource' + if storage_product.billing_resources.empty? + storage_resource = plan.billing_resources.create! product: storage_product + storage_resource.prices.create! price: 0.0001369863, + currency: 'USD', + billing_phase: storage_resource.billing_phases.first, + regions: Region.all + end + + local_disk_product = Product.find_by name: 'temporary-disk', kind: 'resource' + if local_disk_product.billing_resources.empty? + local_disk_resource = plan.billing_resources.create! product: local_disk_product + local_disk_resource.prices.create! price: 0.0001369863, + currency: 'USD', + billing_phase: local_disk_resource.billing_phases.first, + regions: Region.all + end + + bandwidth_product = Product.find_by name: 'bandwidth', kind: 'resource' + if bandwidth_product.billing_resources.empty? + bandwidth_resource = plan.billing_resources.create!(product: bandwidth_product) + bandwidth_resource.prices.create!(price: 0, max_qty: 1024, currency: 'USD', billing_phase: bandwidth_resource.billing_phases.first, regions: Region.all) # First 1TB is free + bandwidth_resource.prices.create!(price: 0.09, max_qty: 10240, currency: 'USD', billing_phase: bandwidth_resource.billing_phases.first, regions: Region.all) # 1TB - 10TB + bandwidth_resource.prices.create!(price: 0.07, max_qty: nil, currency: 'USD', billing_phase: bandwidth_resource.billing_phases.first, regions: Region.all) # 10TB+ + end + + backup_product = Product.find_by resource_kind: 'backup', kind: 'resource' + if backup_product.billing_resources.empty? + backup_resource = plan.billing_resources.create!(product: backup_product) + backup_resource.prices.create!(price: 0.0000684932, currency: 'USD', billing_phase: backup_resource.billing_phases.first, regions: Region.all) + end + BillingResourcePrice.all.each do |i| + i.regions << region unless i.regions.include?(region) + end + + UserGroup.all.each do |g| + g.update(billing_plan: plan) if g.billing_plan.nil? + end + + puts "Setting default settings to dev environment..." + Setting.find_by(name: 'hostname').update value: 'controller.cstacks.local:3005' + Setting.find_by(name: 'cr_le').update value: 'controller.cstacks.local' + Setting.find_by(name: 'registry_base_url').update value: 'registry.cstacks.local' + Setting.find_by(name: 'registry_node').update value: vagrant_vm_ip + Setting.find_by(name: 'registry_ssh_port').update value: '22' + Setting.find_by(name: 'le_validation_server').update value: '192.168.121.1:3005' + Setting.find_by(name: 'cs_bastion_image').update value: 'ghcr.io/computestacks/cs-docker-bastion:latest' + + puts "Creating log & metric clients..." + mc = MetricClient.first.nil? ? MetricClient.create!(endpoint: "http://#{vagrant_vm_ip}:9090") : MetricClient.first + lc = LogClient.first.nil? ? LogClient.create!(endpoint: "http://#{vagrant_vm_ip}:3100") : LogClient.first + region.update metric_client: mc, log_client: lc, loki_endpoint: "http://#{vagrant_vm_ip}:3100" + + if region.nodes.empty? + puts "Creating dev node..." + region.nodes.create! label: 'csdev', + hostname: 'csdev', + primary_ip: vagrant_vm_ip, + public_ip: vagrant_vm_ip, + active: true, + ssh_port: 22, + block_write_bps: 0, + block_read_bps: 0, + block_write_iops: 0, + block_read_iops: 0 + end + + unless Network.where(name: 'dev', network_driver: 'bridge').exists? + puts "Creating network..." + Network.create! subnet: "10.167.0.0/21", + active: true, + name: "dev", + label: "Dev Network", + network_driver: 'bridge', + region: region + end + + + # PowerDNS Setup + if ProvisionDriver.all.empty? + pdns_driver = ProvisionDriver.create!( + endpoint: "http://localhost:#{ENV['PDNS_API_PORT']}/api/v1/servers", + settings: { + config: { + zone_type: 'Native', + masters: [], # When zone_type == 'Master', add the primary NS server here. + nameservers: ['ns1.cstacks.local.'], + server: 'localhost' + } + }, + module_name: 'Pdns', + username: 'admin', + api_key: Secret.encrypt!('d3vE_nv1rnm3n1'), # WebServer PW + api_secret: Secret.encrypt!('d3vE_nv1rnm3n1') # API-Key + ) + + dns_type = ProductModule.create!(name: 'dns', primary: pdns_driver) + pdns_driver.product_modules << dns_type + Dns::Zone.create! name: 'cstacks.local', + provider_ref: 'cstacks.local.', + provision_driver: pdns_driver + end + + + +end diff --git a/lib/tasks/test_connection.rake b/lib/tasks/test_connection.rake index 8012a6f..90cffac 100644 --- a/lib/tasks/test_connection.rake +++ b/lib/tasks/test_connection.rake @@ -37,7 +37,7 @@ namespace :test_connection do next end begin - addr = Diplomat.configuration.options.empty? ? "http://#{n}:8500" : "https://#{n}:8501" + addr = "#{CONSUL_API_PROTO}://#{n}:#{CONSUL_API_PORT}" peers = Diplomat::Status.peers({ http_addr: addr, dc: dc }) puts %Q[Consul Region: #{dc} #{n} - #{peers.is_a?(Array) ? "found #{peers.count} nodes": peers}] rescue diff --git a/lib/test/powerdns/pdns_up.sh b/lib/test/powerdns/pdns_up.sh index 78722ba..12f488c 100755 --- a/lib/test/powerdns/pdns_up.sh +++ b/lib/test/powerdns/pdns_up.sh @@ -6,10 +6,10 @@ pdnsutil add-record csdevserver.local @ CAA "0 issue \"letsencrypt.org\"" pdnsutil create-zone usrcloud.local ns1.cstacks.local pdnsutil add-record usrcloud.local @ A 127.0.0.1 pdnsutil add-record usrcloud.local www CNAME usrcloud.local -pdnsutil create-zone usrca.local ns1.cstacks.local -pdnsutil add-record usrca.local @ A 127.0.0.1 -pdnsutil add-record usrca.local www CNAME usrca.local -pdnsutil add-record usrca.local @ CAA "0 issue \"letsencrypt.org\"" +pdnsutil create-zone usrca.local ns1.cstacks.local +pdnsutil add-record usrca.local @ A 127.0.0.1 +pdnsutil add-record usrca.local www CNAME usrca.local +pdnsutil add-record usrca.local @ CAA "0 issue \"letsencrypt.org\"" pdnsutil create-zone cstacksorg.local ns1.cstacks.local pdnsutil add-record cstacksorg.local @ A 127.0.0.1 pdnsutil create-zone csnet.local ns1.cstacks.local diff --git a/lib/test/system_containers/acme_test_container_concern.rb b/lib/test/system_containers/acme_test_container_concern.rb deleted file mode 100644 index 0014ccc..0000000 --- a/lib/test/system_containers/acme_test_container_concern.rb +++ /dev/null @@ -1,16 +0,0 @@ -module AcmeTestContainerConcern - - def before_setup - super - redirect_output = RUBY_PLATFORM =~ /linux/ ? '>/dev/null 2>/dev/null' : '&>/dev/null' - - unless system("docker info #{redirect_output}") - raise "Error! Ensure docker is running!" - end - - return true if system("docker inspect acme-test #{redirect_output}") # Already running! - `docker run -d --name acme-test --rm -e GODEBUG="tls13=1" -e PEBBLE_VA_ALWAYS_VALID=1 -p 14000:14000 -p 15000:15000 letsencrypt/pebble:latest pebble -config /test/config/pebble-config.json -strict` - sleep(5) - end - -end diff --git a/lib/test/system_containers/consul_test_container_concern.rb b/lib/test/system_containers/consul_test_container_concern.rb index 03a574e..2918411 100644 --- a/lib/test/system_containers/consul_test_container_concern.rb +++ b/lib/test/system_containers/consul_test_container_concern.rb @@ -11,13 +11,13 @@ def before_setup 'size' => i.id * 4096, 'archives' => [] } - Diplomat::Kv.put("borg/repository/#{i.name}", backup_data.to_json) + Diplomat::Kv.put("borg/repository/#{i.name}", backup_data.to_json, { http_addr: "#{CONSUL_API_PROTO}://#{ENV['VAGRANT_VM_IP']}:#{CONSUL_API_PORT}" }) end end def after_teardown Volume.all.each do |i| - Diplomat::Kv.delete "borg/repository/#{i.name}" + Diplomat::Kv.delete "borg/repository/#{i.name}", { http_addr: "#{CONSUL_API_PROTO}://#{ENV['VAGRANT_VM_IP']}:#{CONSUL_API_PORT}" } end super end diff --git a/test/controllers/api/container_services/ingress_rules_controller_test.rb b/test/controllers/api/container_services/ingress_rules_controller_test.rb index b9dcc49..3888622 100644 --- a/test/controllers/api/container_services/ingress_rules_controller_test.rb +++ b/test/controllers/api/container_services/ingress_rules_controller_test.rb @@ -16,8 +16,6 @@ class Api::ContainerServices::IngressRulesControllerTest < ActionDispatch::Integ data = JSON.parse(response.body) - puts data['ingress_rules'] - assert_equal @service.ingress_rules.count, data['ingress_rules'].count end diff --git a/test/controllers/api/networks/ingress_rules/domains_controller_test.rb b/test/controllers/api/networks/ingress_rules/domains_controller_test.rb index 612e7de..d47fe2c 100644 --- a/test/controllers/api/networks/ingress_rules/domains_controller_test.rb +++ b/test/controllers/api/networks/ingress_rules/domains_controller_test.rb @@ -17,7 +17,6 @@ class Api::Networks::IngressRules::DomainsControllerTest < ActionDispatch::Integ get "/api/networks/ingress_rules/#{ingress.id}/domains", as: :json, headers: @basic_auth_headers data = JSON.parse(response.body) - puts data assert_response :success assert_not_empty data["domains"] diff --git a/test/controllers/api/stacks/load_balancers_controller_test.rb b/test/controllers/api/stacks/load_balancers_controller_test.rb index ecd0cc0..ff5c043 100644 --- a/test/controllers/api/stacks/load_balancers_controller_test.rb +++ b/test/controllers/api/stacks/load_balancers_controller_test.rb @@ -26,10 +26,10 @@ class Api::Stacks::LoadBalancersControllerTest < ActionDispatch::IntegrationTest node.container_services.web_only.each do |service| service.ingress_rules.each do |ingress| rule = Digest::MD5.hexdigest("#{service.name}#{ingress.id}") - assert_match /backend #{rule}/, response.body - assert_match /use_backend #{rule}/, response.body + assert_match(/backend #{rule}/, response.body) + assert_match(/use_backend S_#{rule}/, response.body) service.containers.each do |container| - assert_match /server #{container.name} #{container.ip_address.ipaddr}:#{ingress.port}/, response.body + assert_match(/server #{container.name} #{container.ip_address.ipaddr}:#{ingress.port}/, response.body) end end end diff --git a/test/fixtures/load_balancers.yml b/test/fixtures/load_balancers.yml index 82d4b2d..5f01384 100644 --- a/test/fixtures/load_balancers.yml +++ b/test/fixtures/load_balancers.yml @@ -2,7 +2,7 @@ default: name: test-lb01 label: test-lb01 internal_ip: '["127.0.0.1"]' - ext_ip: '["127.0.0.1"]' + ext_ip: '["<%= ENV['VAGRANT_VM_IP'] %>"]' ext_config: '["/etc/haproxy/haproxy.cfg"]' ext_reload_cmd: "bash -c 'if [[ -z $(pidof haproxy) ]]; then service haproxy start; else service haproxy reload; fi;'" public_ip: 127.0.0.1 diff --git a/test/fixtures/log_clients.yml b/test/fixtures/log_clients.yml index 8eb3d7e..b4dd6b0 100644 --- a/test/fixtures/log_clients.yml +++ b/test/fixtures/log_clients.yml @@ -1,2 +1,2 @@ vagrant_log_client: - endpoint: "http://127.0.0.1:3100" + endpoint: "http://<%= ENV['VAGRANT_VM_IP'].blank? ? '127.0.0.1' : ENV['VAGRANT_VM_IP'] %>:3100" diff --git a/test/fixtures/metric_clients.yml b/test/fixtures/metric_clients.yml index ca0c3c2..698612e 100644 --- a/test/fixtures/metric_clients.yml +++ b/test/fixtures/metric_clients.yml @@ -1,2 +1,2 @@ vagrant_metric_client: - endpoint: "http://127.0.0.1:9090" + endpoint: "http://<%= ENV['VAGRANT_VM_IP'].blank? ? '127.0.0.1' : ENV['VAGRANT_VM_IP'] %>:9090" diff --git a/test/fixtures/nodes.yml b/test/fixtures/nodes.yml index e444d2a..bcd9501 100644 --- a/test/fixtures/nodes.yml +++ b/test/fixtures/nodes.yml @@ -2,7 +2,7 @@ testone: label: 'test01' hostname: 'test01' public_ip: '127.0.0.1' - primary_ip: '127.0.0.1' + primary_ip: <%= ENV['VAGRANT_VM_IP'] %> region: regionone active: true diff --git a/test/fixtures/provision_drivers.yml b/test/fixtures/provision_drivers.yml index a7bfa7f..bfa62ba 100644 --- a/test/fixtures/provision_drivers.yml +++ b/test/fixtures/provision_drivers.yml @@ -1,7 +1,7 @@ powerdns: - endpoint: "http://localhost:8081/api/v1/servers" + endpoint: "http://localhost:<%= ENV['PDNS_API_PORT'] %>/api/v1/servers" settings: '<%= { config: { zone_type: "Native", masters: [], nameservers: ["ns1.stacks.local."], server: "localhost" } } %>' module_name: "Pdns" username: "Admin" - api_key: '<%= Secret.encrypt! File.read("/home/vagrant/.pdns/web_auth_pass").gsub("\n","") %>' - api_secret: '<%= Secret.encrypt! File.read("/home/vagrant/.pdns/api_key").gsub("\n","") %>' + api_key: '<%= Secret.encrypt! "d3vE_nv1rnm3n1" %>' + api_secret: '<%= Secret.encrypt! "d3vE_nv1rnm3n1" %>' diff --git a/test/fixtures/regions.yml b/test/fixtures/regions.yml index b0e16c7..968e77b 100644 --- a/test/fixtures/regions.yml +++ b/test/fixtures/regions.yml @@ -6,7 +6,7 @@ regionone: network_driver: bridge log_client: vagrant_log_client metric_client: vagrant_metric_client - consul_token: '<%= File.exist?("/home/vagrant/consul.token") ? File.read("/home/vagrant/consul.token").gsub("\n","") : "" %>' + consul_token: '<%= File.exist?("/home/vagrant/consul.token") ? File.read("/home/vagrant/consul.token").strip : File.read("#{Rails.root}/#{ENV['CONSUL_TOKEN_PATH']}") %>' #regiontwo: # location: testlocation # name: dev2 diff --git a/test/integration/lets_encrypt_flow_test.rb b/test/integration/lets_encrypt_flow_test.rb index bee4dce..ded5f7a 100644 --- a/test/integration/lets_encrypt_flow_test.rb +++ b/test/integration/lets_encrypt_flow_test.rb @@ -9,8 +9,6 @@ # class LetsEncryptFlowTest < ActionDispatch::IntegrationTest - include AcmeTestContainerConcern - test 'can enable lets encrypt on a domain' do Sidekiq::Testing.inline! do # Create the domain and enable Lets Encrypt diff --git a/test/integration/service_provision_flow_test.rb b/test/integration/service_provision_flow_test.rb index 1149195..b266a7d 100644 --- a/test/integration/service_provision_flow_test.rb +++ b/test/integration/service_provision_flow_test.rb @@ -12,6 +12,7 @@ class ServiceProvisionFlowTest < ActionDispatch::IntegrationTest order_session.images.each do |image| image[:package_id] = products(:containersmall).id next unless image[:image_id] == container_image_image_variants(:wordpress_default).container_image.id + image[:params]['username'][:value] = 'devuser' end @@ -46,9 +47,11 @@ class ServiceProvisionFlowTest < ActionDispatch::IntegrationTest Sidekiq::Testing.inline! do order_prov_success = order_process.perform puts order_process.errors.join(" ") unless order_process.errors.empty? - event.event_details.each do |i| - puts i.data - end unless order_prov_success + unless order_prov_success + event.event_details.each do |i| + puts i.data + end + end assert order_prov_success end diff --git a/test/integration/service_usage/aggregate_usage_test.rb b/test/integration/service_usage/aggregate_usage_test.rb index 54c9a92..26509c8 100644 --- a/test/integration/service_usage/aggregate_usage_test.rb +++ b/test/integration/service_usage/aggregate_usage_test.rb @@ -19,21 +19,21 @@ class AggregateUsageTest < ActionDispatch::IntegrationTest ## # Basic sanity checking - expected_keys = %w( - subscription_id - subscription_product_id - product - billing_resource - container_service_id - container_id - user - external_id - total - qty - period_start - period_end - usage_items - ) + expected_keys = %w[ + subscription_id + subscription_product_id + product + billing_resource + container_service_id + container_id + user + external_id + total + qty + period_start + period_end + usage_items + ] assert_empty expected_keys - group.keys refute_nil group.dig(:product, :id) @@ -42,6 +42,7 @@ class AggregateUsageTest < ActionDispatch::IntegrationTest group.keys.each do |k| next if k == "external_id" + refute_nil group[k] end diff --git a/test/integration/service_usage/collect_usage_test.rb b/test/integration/service_usage/collect_usage_test.rb index be40618..08758e5 100644 --- a/test/integration/service_usage/collect_usage_test.rb +++ b/test/integration/service_usage/collect_usage_test.rb @@ -11,9 +11,7 @@ class CollectUsageTest < ActionDispatch::IntegrationTest # Note: This currently assumes 1 container per service. BillingUsage.all.each do |i| - if i.total > 0 - refute i.processed - end + refute i.processed if i.total.positive? # We must have a subscription product! refute_nil i.subscription_product @@ -44,19 +42,16 @@ class CollectUsageTest < ActionDispatch::IntegrationTest sum + (id * 1024) end expected_amount = expected_amount.zero? ? expected_amount : (expected_amount / BYTE_TO_GB).round(4) - if expected_amount != i.qty - puts "ID: #{i.id} | E: #{expected_amount} | A: #{i.qty}" - end + puts "ID: #{i.id} | E: #{expected_amount} | A: #{i.qty}" if expected_amount != i.qty assert_equal expected_amount, i.qty when 'storage' next if i.subscription.linked_obj.service.volumes.empty? + # add all IDs together expected_usage = i.subscription.linked_obj.service.volumes.pluck(:id).inject(:+) # deduct what's included in their plan - expected_usage = expected_usage - i.subscription.package.storage - if expected_usage != i.qty - puts "ID: #{i.id} | E: #{expected_usage} | A: #{i.qty}" - end + expected_usage -= i.subscription.package.storage + puts "ID: #{i.id} | E: #{expected_usage} | A: #{i.qty}" if expected_usage != i.qty assert_equal expected_usage, i.qty end diff --git a/test/models/deployment/container_domain_test.rb b/test/models/deployment/container_domain_test.rb index 663915a..bf25cb5 100644 --- a/test/models/deployment/container_domain_test.rb +++ b/test/models/deployment/container_domain_test.rb @@ -7,8 +7,6 @@ # class Deployment::ContainerDomainTest < ActiveSupport::TestCase - include AcmeTestContainerConcern - test 'can list all container domains' do d = Deployment::ContainerDomain.where.not(user: nil).first diff --git a/test/models/lets_encrypt_account_test.rb b/test/models/lets_encrypt_account_test.rb index 98bb1b8..8f07432 100644 --- a/test/models/lets_encrypt_account_test.rb +++ b/test/models/lets_encrypt_account_test.rb @@ -4,8 +4,6 @@ # Test creating a LetsEncrypt Account class LetsEncryptAccountTest < ActiveSupport::TestCase - include AcmeTestContainerConcern - test 'can find new account' do new_account = LetsEncryptAccount.find_or_create refute_nil new_account diff --git a/test/models/load_balancer_test.rb b/test/models/load_balancer_test.rb index edb627f..add8388 100644 --- a/test/models/load_balancer_test.rb +++ b/test/models/load_balancer_test.rb @@ -2,8 +2,6 @@ class LoadBalancerTest < ActiveSupport::TestCase - include AcmeTestContainerConcern - setup do @lb = load_balancers(:default) end diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..fb57ccd --- /dev/null +++ b/yarn.lock @@ -0,0 +1,4 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + +