From 074778d0b021afefdffed1931228954f9adbd9f6 Mon Sep 17 00:00:00 2001 From: Gene Liverman Date: Tue, 30 Nov 2021 17:01:17 -0500 Subject: [PATCH] Initial commit --- .github/dependabot.yml | 20 ++ .gitignore | 3 + LICENSE | 201 ++++++++++++++++++ README.md | 7 + Vagrantfile | 17 ++ docker/dev/.dockerignore | 3 + docker/dev/Dockerfile | 36 ++++ docker/dev/Dockerfile-aio | 39 ++++ docker/dev/docker-compose.yml | 73 +++++++ docker/dev/docker-entrypoint.sh | 6 + docker/test-all-providers/.bundle/config | 2 + docker/test-all-providers/.dockerignore | 3 + docker/test-all-providers/Dockerfile | 34 +++ docker/test-all-providers/Gemfile | 4 + docker/test-all-providers/Gemfile.lock | 141 ++++++++++++ docker/test-all-providers/docker-compose.yml | 73 +++++++ .../test-all-providers/docker-entrypoint.sh | 6 + .../test-all-providers/update-gemfile-lock.sh | 6 + helm/vmpooler/.helmignore | 23 ++ helm/vmpooler/Chart.lock | 6 + helm/vmpooler/Chart.yaml | 10 + helm/vmpooler/charts/redis-10.7.12.tgz | Bin 0 -> 59110 bytes helm/vmpooler/templates/NOTES.txt | 21 ++ helm/vmpooler/templates/_helpers.tpl | 63 ++++++ .../configmap-vmpooler-baseconfig.yaml | 20 ++ .../templates/configmap-vmpooler-pools.yaml | 11 + helm/vmpooler/templates/deployment-api.yaml | 122 +++++++++++ .../templates/deployment-manager.yaml | 162 ++++++++++++++ helm/vmpooler/templates/ingress.yaml | 33 +++ helm/vmpooler/templates/service-api.yaml | 19 ++ helm/vmpooler/templates/service-manager.yaml | 19 ++ helm/vmpooler/templates/serviceaccount.yaml | 12 ++ .../templates/tests/test-connection.yaml | 15 ++ helm/vmpooler/values.yaml | 171 +++++++++++++++ 34 files changed, 1381 insertions(+) create mode 100644 .github/dependabot.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Vagrantfile create mode 100644 docker/dev/.dockerignore create mode 100644 docker/dev/Dockerfile create mode 100644 docker/dev/Dockerfile-aio create mode 100644 docker/dev/docker-compose.yml create mode 100644 docker/dev/docker-entrypoint.sh create mode 100644 docker/test-all-providers/.bundle/config create mode 100644 docker/test-all-providers/.dockerignore create mode 100644 docker/test-all-providers/Dockerfile create mode 100644 docker/test-all-providers/Gemfile create mode 100644 docker/test-all-providers/Gemfile.lock create mode 100644 docker/test-all-providers/docker-compose.yml create mode 100644 docker/test-all-providers/docker-entrypoint.sh create mode 100755 docker/test-all-providers/update-gemfile-lock.sh create mode 100644 helm/vmpooler/.helmignore create mode 100644 helm/vmpooler/Chart.lock create mode 100644 helm/vmpooler/Chart.yaml create mode 100644 helm/vmpooler/charts/redis-10.7.12.tgz create mode 100644 helm/vmpooler/templates/NOTES.txt create mode 100644 helm/vmpooler/templates/_helpers.tpl create mode 100644 helm/vmpooler/templates/configmap-vmpooler-baseconfig.yaml create mode 100644 helm/vmpooler/templates/configmap-vmpooler-pools.yaml create mode 100644 helm/vmpooler/templates/deployment-api.yaml create mode 100644 helm/vmpooler/templates/deployment-manager.yaml create mode 100644 helm/vmpooler/templates/ingress.yaml create mode 100644 helm/vmpooler/templates/service-api.yaml create mode 100644 helm/vmpooler/templates/service-manager.yaml create mode 100644 helm/vmpooler/templates/serviceaccount.yaml create mode 100644 helm/vmpooler/templates/tests/test-connection.yaml create mode 100644 helm/vmpooler/values.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..4b3e5bd --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 +updates: +- package-ecosystem: bundler + directory: "/docker/test-all-providers/" + schedule: + interval: daily + time: "13:00" + open-pull-requests-limit: 10 +- package-ecosystem: docker + directory: "/docker/dev/" + schedule: + interval: daily + time: "13:00" + open-pull-requests-limit: 10 +- package-ecosystem: docker + directory: "/docker/test-all-providers/" + schedule: + interval: daily + time: "13:00" + open-pull-requests-limit: 10 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5164f2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/*/**/vendor/bundle/ +/docker/**/vmpooler.yaml +.vagrant/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..080caf1 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# vmpooler-deployment + +This repo contains Dockerfiles and a Helm chart that can be used to deploy [VMPooler](https://github.com/puppetlabs/vmpooler). + +## Status + +This repo is a work in progress. It mostly works but many bits need adjusting here and there as they were compiled from other locations. More details will be added soon. diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..a230820 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,17 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure("2") do |config| + config.vm.box = "genebean/almalinux-8-docker-ce" + config.vm.network "forwarded_port", guest: 8080, host: 8080 # VMPooler api in docker-compose + config.vm.network "forwarded_port", guest: 8081, host: 8081 # VMPooler manager in docker-compose + config.vm.network "forwarded_port", guest: 8082, host: 8082 # Jaeger in docker-compose + config.vm.provision "shell", inline: <<-SCRIPT + mkdir /var/log/vmpooler + chown vagrant:vagrant /var/log/vmpooler + curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose + docker-compose --version + SCRIPT +end diff --git a/docker/dev/.dockerignore b/docker/dev/.dockerignore new file mode 100644 index 0000000..8dad93e --- /dev/null +++ b/docker/dev/.dockerignore @@ -0,0 +1,3 @@ +.bundle/ +vendor/ +update-gemfile-lock.sh diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile new file mode 100644 index 0000000..2480e16 --- /dev/null +++ b/docker/dev/Dockerfile @@ -0,0 +1,36 @@ +# Run vmpooler in a Docker container! Configuration can either be embedded +# and built within the current working directory, or stored in a +# VMPOOLER_CONFIG environment value and passed to the Docker daemon. +# +# BUILD: +# docker build -t vmpooler . +# +# RUN: +# docker run -e VMPOOLER_CONFIG -p 80:4567 -it vmpooler + +FROM jruby:9.2-jdk + +ENV RACK_ENV=production + +RUN apt-get update -qq && \ + apt-get install -y --no-install-recommends make && \ + apt-get clean autoclean && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* + +COPY docker/docker-entrypoint.sh /usr/local/bin/ +COPY ./Gemfile ./ +COPY ./vmpooler.gemspec ./ +COPY ./lib/vmpooler/version.rb ./lib/vmpooler/version.rb + +RUN gem install bundler && \ + bundle config set --local jobs 3 && \ + bundle install + +COPY ./ ./ + +RUN gem build vmpooler.gemspec && \ + gem install vmpooler*.gem && \ + chmod +x /usr/local/bin/docker-entrypoint.sh + +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/docker/dev/Dockerfile-aio b/docker/dev/Dockerfile-aio new file mode 100644 index 0000000..48a62d6 --- /dev/null +++ b/docker/dev/Dockerfile-aio @@ -0,0 +1,39 @@ +# Run vmpooler in a Docker container! Configuration can either be embedded +# and built within the current working directory, or stored in a +# VMPOOLER_CONFIG environment value and passed to the Docker daemon. +# +# BUILD: +# docker build -t vmpooler . +# +# RUN: +# docker run -e VMPOOLER_CONFIG -p 80:4567 -it vmpooler + +FROM jruby:9.2.9-jdk + +RUN mkdir -p /var/lib/vmpooler + +WORKDIR /var/lib/vmpooler + +RUN echo "deb http://httpredir.debian.org/debian jessie main" >/etc/apt/sources.list.d/jessie-main.list + +RUN apt-get update -qq && \ + apt-get install -y --no-install-recommends make redis-server && \ + apt-get clean autoclean && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* + +ADD Gemfile* /var/lib/vmpooler/ + +RUN bundle install --system + +RUN ln -s /opt/jruby/bin/jruby /usr/bin/jruby + +COPY . /var/lib/vmpooler + +ENV VMPOOLER_LOG /var/log/vmpooler.log + +CMD \ + /etc/init.d/redis-server start \ + && /var/lib/vmpooler/scripts/vmpooler_init.sh start \ + && while [ ! -f ${VMPOOLER_LOG} ]; do sleep 1; done ; \ + tail -f ${VMPOOLER_LOG} diff --git a/docker/dev/docker-compose.yml b/docker/dev/docker-compose.yml new file mode 100644 index 0000000..21b179d --- /dev/null +++ b/docker/dev/docker-compose.yml @@ -0,0 +1,73 @@ +# For local development run with a dummy provider +version: '3.8' +services: + vmpooler-api: + build: + context: ../ + dockerfile: docker/Dockerfile_local + volumes: + - type: bind + source: ${PWD}/vmpooler.yaml + target: /etc/vmpooler/vmpooler.yaml + ports: + - "8080:4567" + networks: + - redis-net + environment: + - VMPOOLER_DEBUG=true # for use of dummy auth + - VMPOOLER_CONFIG_FILE=/etc/vmpooler/vmpooler.yaml + - REDIS_SERVER=redislocal + - LOGFILE=/dev/null + - JRUBY_OPTS=-Xinvokedynamic.yield=false + - VMPOOLER_TRACING_ENABLED=true + - VMPOOLER_TRACING_JAEGER_HOST=http://jaeger-aio:14268/api/traces + image: vmpooler-local + command: api + depends_on: + - redislocal + vmpooler-manager: + build: + context: ../ + dockerfile: docker/Dockerfile_local + volumes: + - type: bind + source: ${PWD}/vmpooler.yaml + target: /etc/vmpooler/vmpooler.yaml + ports: + - "8081:4567" + networks: + - redis-net + environment: + - VMPOOLER_DEBUG=true # for use of dummy auth + - VMPOOLER_CONFIG_FILE=/etc/vmpooler/vmpooler.yaml + - REDIS_SERVER=redislocal + - LOGFILE=/dev/null + - JRUBY_OPTS=-Xinvokedynamic.yield=false + - VMPOOLER_TRACING_ENABLED=true + - VMPOOLER_TRACING_JAEGER_HOST=http://jaeger-aio:14268/api/traces + image: vmpooler-local + command: manager + depends_on: + - redislocal + redislocal: + image: redis + # Uncomment this if you don't want the redis data to persist + #command: "redis-server --save '' --appendonly no" + ports: + - "6379:6379" + networks: + - redis-net + jaeger-aio: + image: jaegertracing/all-in-one:1.18 + ports: + - "14250:14250" + - "8082:16686" + networks: + - redis-net + user: '1001' + read_only: true + cap_drop: + - ALL + +networks: + redis-net: diff --git a/docker/dev/docker-entrypoint.sh b/docker/dev/docker-entrypoint.sh new file mode 100644 index 0000000..07d11de --- /dev/null +++ b/docker/dev/docker-entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e + +set -- bundle exec vmpooler "$@" + +exec "$@" diff --git a/docker/test-all-providers/.bundle/config b/docker/test-all-providers/.bundle/config new file mode 100644 index 0000000..2369228 --- /dev/null +++ b/docker/test-all-providers/.bundle/config @@ -0,0 +1,2 @@ +--- +BUNDLE_PATH: "vendor/bundle" diff --git a/docker/test-all-providers/.dockerignore b/docker/test-all-providers/.dockerignore new file mode 100644 index 0000000..8dad93e --- /dev/null +++ b/docker/test-all-providers/.dockerignore @@ -0,0 +1,3 @@ +.bundle/ +vendor/ +update-gemfile-lock.sh diff --git a/docker/test-all-providers/Dockerfile b/docker/test-all-providers/Dockerfile new file mode 100644 index 0000000..6c1c590 --- /dev/null +++ b/docker/test-all-providers/Dockerfile @@ -0,0 +1,34 @@ +# Run vmpooler in a Docker container! Configuration can either be embedded +# and built within the current working directory, or stored in a +# VMPOOLER_CONFIG environment value and passed to the Docker daemon. +# +# BUILD: +# docker build -t vmpooler . +# +# RUN: +# docker run -e VMPOOLER_CONFIG -p 80:4567 -it vmpooler + +FROM jruby:9.2-jdk + +ENV RACK_ENV=production + +ENV LOGFILE=/dev/stdout \ + RACK_ENV=production + +RUN apt-get update -qq && \ + apt-get install -y --no-install-recommends make && \ + apt-get clean autoclean && \ + apt-get autoremove -y && \ + rm -rf /var/lib/apt/lists/* + +COPY ./docker-entrypoint.sh /usr/local/bin/ + +RUN chmod +x /usr/local/bin/docker-entrypoint.sh + +COPY ./Gemfile* ./ + +RUN gem install bundler && \ + bundle config set --local jobs 3 && \ + bundle install + +ENTRYPOINT ["docker-entrypoint.sh"] diff --git a/docker/test-all-providers/Gemfile b/docker/test-all-providers/Gemfile new file mode 100644 index 0000000..e29590a --- /dev/null +++ b/docker/test-all-providers/Gemfile @@ -0,0 +1,4 @@ +source ENV['GEM_SOURCE'] || 'https://rubygems.org' + +gem 'vmpooler', git: 'https://github.com/puppetlabs/vmpooler.git', branch: 'extract-vsphere-provider' +gem 'vmpooler-vsphere-provider', git: 'https://github.com/puppetlabs/vmpooler-provider-vsphere.git' diff --git a/docker/test-all-providers/Gemfile.lock b/docker/test-all-providers/Gemfile.lock new file mode 100644 index 0000000..d2d58ee --- /dev/null +++ b/docker/test-all-providers/Gemfile.lock @@ -0,0 +1,141 @@ +GIT + remote: https://github.com/puppetlabs/vmpooler-provider-vsphere.git + revision: a08cba099f867b1db01a50940ec3ae9239245db5 + specs: + vmpooler-vsphere-provider (1.3.0) + rbvmomi (>= 2.1, < 4.0) + +GIT + remote: https://github.com/puppetlabs/vmpooler.git + revision: d1c86eaf9b8fc1945213dad009db1172dffac7f2 + branch: extract-vsphere-provider + specs: + vmpooler (1.3.0) + concurrent-ruby (~> 1.1) + connection_pool (~> 2.2) + net-ldap (~> 0.16) + nokogiri (~> 1.10) + opentelemetry-exporter-jaeger (= 0.17.0) + opentelemetry-instrumentation-concurrent_ruby (= 0.17.0) + opentelemetry-instrumentation-redis (= 0.17.0) + opentelemetry-instrumentation-sinatra (= 0.17.0) + opentelemetry-resource_detectors (= 0.17.0) + opentelemetry-sdk (= 0.17.0) + pickup (~> 0.0.11) + prometheus-client (~> 2.0) + puma (~> 5.0, >= 5.0.4) + rack (~> 2.2) + rake (~> 13.0) + redis (~> 4.1) + sinatra (~> 2.0) + spicy-proton (~> 2.1) + statsd-ruby (~> 1.4) + +GEM + remote: https://rubygems.org/ + specs: + bindata (2.4.10) + builder (3.2.4) + concurrent-ruby (1.1.9) + connection_pool (2.2.5) + faraday (1.8.0) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0.1) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.1) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + multipart-post (>= 1.2, < 3) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-httpclient (1.0.1) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + google-cloud-env (1.5.0) + faraday (>= 0.17.3, < 2.0) + json (2.6.1) + json (2.6.1-java) + multipart-post (2.1.1) + mustermann (1.1.1) + ruby2_keywords (~> 0.0.1) + net-ldap (0.17.0) + nio4r (2.5.8) + nio4r (2.5.8-java) + nokogiri (1.12.5-java) + racc (~> 1.4) + nokogiri (1.12.5-x86_64-linux) + racc (~> 1.4) + opentelemetry-api (0.17.0) + opentelemetry-common (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-exporter-jaeger (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-common (~> 0.17.0) + opentelemetry-sdk (~> 0.17.0) + thrift + opentelemetry-instrumentation-base (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-instrumentation-concurrent_ruby (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-instrumentation-base (~> 0.17.0) + opentelemetry-instrumentation-redis (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-common (~> 0.17.0) + opentelemetry-instrumentation-base (~> 0.17.0) + opentelemetry-instrumentation-sinatra (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-instrumentation-base (~> 0.17.0) + opentelemetry-resource_detectors (0.17.0) + google-cloud-env + opentelemetry-sdk + opentelemetry-sdk (0.17.0) + opentelemetry-api (~> 0.17.0) + opentelemetry-common (~> 0.17.0) + opentelemetry-instrumentation-base (~> 0.17.0) + optimist (3.0.1) + pickup (0.0.11) + prometheus-client (2.1.0) + puma (5.5.2) + nio4r (~> 2.0) + puma (5.5.2-java) + nio4r (~> 2.0) + racc (1.6.0) + racc (1.6.0-java) + rack (2.2.3) + rack-protection (2.1.0) + rack + rake (13.0.6) + rbvmomi (3.0.0) + builder (~> 3.2) + json (~> 2.3) + nokogiri (~> 1.10) + optimist (~> 3.0) + redis (4.5.1) + ruby2_keywords (0.0.5) + sinatra (2.1.0) + mustermann (~> 1.0) + rack (~> 2.2) + rack-protection (= 2.1.0) + tilt (~> 2.0) + spicy-proton (2.1.13) + bindata (~> 2.3) + statsd-ruby (1.5.0) + thrift (0.15.0) + tilt (2.0.10) + +PLATFORMS + universal-java-1.8 + x86_64-linux + +DEPENDENCIES + vmpooler! + vmpooler-vsphere-provider! + +BUNDLED WITH + 2.2.32 diff --git a/docker/test-all-providers/docker-compose.yml b/docker/test-all-providers/docker-compose.yml new file mode 100644 index 0000000..415a04c --- /dev/null +++ b/docker/test-all-providers/docker-compose.yml @@ -0,0 +1,73 @@ +# For local development run with a dummy provider +version: '3.8' +services: + vmpooler-api: + build: + context: ./ + dockerfile: Dockerfile + volumes: + - type: bind + source: ${PWD}/vmpooler.yaml + target: /etc/vmpooler/vmpooler.yaml + ports: + - "8080:4567" + networks: + - redis-net + environment: + - VMPOOLER_DEBUG=true # for use of dummy auth + - VMPOOLER_CONFIG_FILE=/etc/vmpooler/vmpooler.yaml + - REDIS_SERVER=redislocal + - LOGFILE=/dev/null + - JRUBY_OPTS=-Xinvokedynamic.yield=false + - VMPOOLER_TRACING_ENABLED=true + - VMPOOLER_TRACING_JAEGER_HOST=http://jaeger-aio:14268/api/traces + image: vmpooler-local + command: api + depends_on: + - redislocal + vmpooler-manager: + build: + context: ./ + dockerfile: Dockerfile + volumes: + - type: bind + source: ${PWD}/vmpooler.yaml + target: /etc/vmpooler/vmpooler.yaml + ports: + - "8081:4567" + networks: + - redis-net + environment: + - VMPOOLER_DEBUG=true # for use of dummy auth + - VMPOOLER_CONFIG_FILE=/etc/vmpooler/vmpooler.yaml + - REDIS_SERVER=redislocal + - LOGFILE=/dev/null + - JRUBY_OPTS=-Xinvokedynamic.yield=false + - VMPOOLER_TRACING_ENABLED=true + - VMPOOLER_TRACING_JAEGER_HOST=http://jaeger-aio:14268/api/traces + image: vmpooler-local + command: manager + depends_on: + - redislocal + redislocal: + image: redis + # Uncomment this if you don't want the redis data to persist + #command: "redis-server --save '' --appendonly no" + ports: + - "6379:6379" + networks: + - redis-net + jaeger-aio: + image: jaegertracing/all-in-one:1.18 + ports: + - "14250:14250" + - "8082:16686" + networks: + - redis-net + user: '1001' + read_only: true + cap_drop: + - ALL + +networks: + redis-net: diff --git a/docker/test-all-providers/docker-entrypoint.sh b/docker/test-all-providers/docker-entrypoint.sh new file mode 100644 index 0000000..07d11de --- /dev/null +++ b/docker/test-all-providers/docker-entrypoint.sh @@ -0,0 +1,6 @@ +#!/bin/sh +set -e + +set -- bundle exec vmpooler "$@" + +exec "$@" diff --git a/docker/test-all-providers/update-gemfile-lock.sh b/docker/test-all-providers/update-gemfile-lock.sh new file mode 100755 index 0000000..a148d6c --- /dev/null +++ b/docker/test-all-providers/update-gemfile-lock.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +docker run -it --rm \ + -v $(pwd):/app \ + $(grep ^FROM ./Dockerfile |cut -d ' ' -f2) \ + /bin/bash -c 'apt-get update -qq && apt-get install -y --no-install-recommends make && cd /app && gem install bundler && bundle install --jobs 3' diff --git a/helm/vmpooler/.helmignore b/helm/vmpooler/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm/vmpooler/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm/vmpooler/Chart.lock b/helm/vmpooler/Chart.lock new file mode 100644 index 0000000..6f88888 --- /dev/null +++ b/helm/vmpooler/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: redis + repository: https://charts.bitnami.com/bitnami + version: 10.7.12 +digest: sha256:cd2b6498209e347387f3467403cb063d93a44fdd345cd75fb090eed1eb8debee +generated: "2020-08-03T16:14:08.491207-07:00" diff --git a/helm/vmpooler/Chart.yaml b/helm/vmpooler/Chart.yaml new file mode 100644 index 0000000..cee97a2 --- /dev/null +++ b/helm/vmpooler/Chart.yaml @@ -0,0 +1,10 @@ +apiVersion: v2 +name: vmpooler +description: A Helm chart to deploy vmpooler +type: application +version: 1.5.2 +appVersion: 1.3.0 +dependencies: + - name: redis + repository: https://charts.bitnami.com/bitnami + version: 10.7.12 diff --git a/helm/vmpooler/charts/redis-10.7.12.tgz b/helm/vmpooler/charts/redis-10.7.12.tgz new file mode 100644 index 0000000000000000000000000000000000000000..1ff3e4ed9827e67dc7a5d7a6c9326b09dbf19a52 GIT binary patch literal 59110 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 AbN~PV literal 0 HcmV?d00001 diff --git a/helm/vmpooler/templates/NOTES.txt b/helm/vmpooler/templates/NOTES.txt new file mode 100644 index 0000000..0c78799 --- /dev/null +++ b/helm/vmpooler/templates/NOTES.txt @@ -0,0 +1,21 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "vmpooler.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "vmpooler.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "vmpooler.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "vmpooler.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80 +{{- end }} diff --git a/helm/vmpooler/templates/_helpers.tpl b/helm/vmpooler/templates/_helpers.tpl new file mode 100644 index 0000000..6772371 --- /dev/null +++ b/helm/vmpooler/templates/_helpers.tpl @@ -0,0 +1,63 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "vmpooler.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "vmpooler.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "vmpooler.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Common labels +*/}} +{{- define "vmpooler.labels" -}} +helm.sh/chart: {{ include "vmpooler.chart" . }} +{{ include "vmpooler.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Selector labels +*/}} +{{- define "vmpooler.selectorLabels" -}} +app.kubernetes.io/name: {{ include "vmpooler.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "vmpooler.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "vmpooler.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} diff --git a/helm/vmpooler/templates/configmap-vmpooler-baseconfig.yaml b/helm/vmpooler/templates/configmap-vmpooler-baseconfig.yaml new file mode 100644 index 0000000..e914e3a --- /dev/null +++ b/helm/vmpooler/templates/configmap-vmpooler-baseconfig.yaml @@ -0,0 +1,20 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: vmpooler-baseconfig +data: + vmpooler.yaml: | + --- + {{- with .Values.baseconfig_extras }} + {{- toYaml . | nindent 4 }} + {{- end }} + + :tagfilter: + project: '^([^/]+)' + + :auth: + {{- with .Values.auth }} + {{- toYaml . | nindent 6 }} + {{- end }} + :prometheus: + prefix: {{ .Values.vmpoolerInstance }} diff --git a/helm/vmpooler/templates/configmap-vmpooler-pools.yaml b/helm/vmpooler/templates/configmap-vmpooler-pools.yaml new file mode 100644 index 0000000..193c5f0 --- /dev/null +++ b/helm/vmpooler/templates/configmap-vmpooler-pools.yaml @@ -0,0 +1,11 @@ +{{- if eq .Values.poolsConfigMapName "vmpooler-pools" -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: vmpooler-pools +data: + pools.yaml: | + --- + :pools: + {{- toYaml .Values.pools | nindent 6 }} +{{- end -}} \ No newline at end of file diff --git a/helm/vmpooler/templates/deployment-api.yaml b/helm/vmpooler/templates/deployment-api.yaml new file mode 100644 index 0000000..7a6d56c --- /dev/null +++ b/helm/vmpooler/templates/deployment-api.yaml @@ -0,0 +1,122 @@ +{{- $fullName := include "vmpooler.fullname" . -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $fullName }}-api + labels: + app: {{ $fullName }}-api + redis-client: "true" + {{- include "vmpooler.labels" . | nindent 4 }} + test: liveness + annotations: + configmap.reloader.stakater.com/reload: "{{ .Values.configmapName }},vmpooler-pools" + secret.reloader.stakater.com/reload: 'redis' +spec: + replicas: {{ .Values.api.replicaCount }} + selector: + matchLabels: + app: {{ $fullName }}-api + template: + metadata: + labels: + app: {{ $fullName }}-api + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "vmpooler.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ $fullName }}-api + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ .Values.service.port }} + resources: + {{- toYaml .Values.api.resources | nindent 12 }} + args: + - 'api' + livenessProbe: + httpGet: + path: /api/v1/status + port: {{ .Values.service.port }} + initialDelaySeconds: 40 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /api/v1/status + port: {{ .Values.service.port }} + initialDelaySeconds: 40 + periodSeconds: 10 + env: + - name: JRUBY_OPTS + value: '-J-XX:+UseG1GC' + - name: PORT + value: {{ .Values.service.port | quote }} + - name: SITE_NAME + value: '{{ .Values.vmpoolerInstance }}.{{ .Values.ingress.domain }}' + - name: DOMAIN + value: {{ .Values.sut_domain }} + - name: EXPERIMENTAL_FEATURES + value: 'true' + - name: USAGE_STATS + value: '{{ .Values.usage_stats }}' + - name: VMPOOLER_CONFIG_FILE + value: /etc/vmpooler/vmpooler.yaml + - name: VMPOOLER_TRACING_ENABLED + value: '{{ .Values.tracing.enabled }}' + - name: VMPOOLER_TRACING_JAEGER_HOST + value: '{{ .Values.tracing.jaegerHost }}' + - name: EXTRA_CONFIG + value: '/etc/vmpooler/config/pools.yaml' + - name: REDIS_SERVER + value: 'redis-master' + - name: ONDEMAND_REQUEST_TTL + value: "50" + - name: ONDEMAND_CLONE_LIMIT + value: "50" + - name: MAX_LIFETIME_UPPER_LIMIT + value: "336" + - name: VM_LIFETIME + value: '2' + - name: VM_LIFETIME_AUTH + value: '12' + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis + key: redis-password + volumeMounts: + - name: config-volume + mountPath: /etc/vmpooler + - name: pools-config + mountPath: /etc/vmpooler/config + volumes: + - name: config-volume + configMap: + name: {{ .Values.configmapName }} + items: + - key: vmpooler.yaml + path: vmpooler.yaml + - name: pools-config + configMap: + name: vmpooler-pools + items: + - key: pools.yaml + path: pools.yaml + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/vmpooler/templates/deployment-manager.yaml b/helm/vmpooler/templates/deployment-manager.yaml new file mode 100644 index 0000000..6020109 --- /dev/null +++ b/helm/vmpooler/templates/deployment-manager.yaml @@ -0,0 +1,162 @@ +{{- $fullName := include "vmpooler.fullname" . -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ $fullName }}-manager + labels: + app: {{ $fullName }}-manager + redis-client: "true" + {{- include "vmpooler.labels" . | nindent 4 }} + test: liveness + annotations: + configmap.reloader.stakater.com/reload: "{{ .Values.configmapName }},vmpooler-pools" + secret.reloader.stakater.com/reload: 'redis' +spec: + replicas: 1 + selector: + matchLabels: + app: {{ $fullName }}-manager + strategy: + type: Recreate + template: + metadata: + labels: + app: {{ $fullName }}-manager + {{- with .Values.manager.annotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "vmpooler.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ $fullName }}-manager + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - containerPort: {{ .Values.service.port }} + resources: + {{- toYaml .Values.manager.resources | nindent 12 }} + args: + - 'manager' + livenessProbe: + exec: + command: + - /bin/sh + - -c + - ps -p 1 > /dev/null + initialDelaySeconds: 40 + periodSeconds: 10 + readinessProbe: + exec: + command: + - /bin/sh + - -c + - ps -p 1 > /dev/null + initialDelaySeconds: 40 + periodSeconds: 10 + env: + - name: JRUBY_OPTS + value: '-J-XX:+UseG1GC -Xinvokedynamic.yield=false' + - name: TASK_LIMIT + value: '30' + - name: MIGRATION_LIMIT + value: '10' + - name: VM_LIFETIME + value: '2' + - name: VM_LIFETIME_AUTH + value: '12' + - name: VM_CHECKTIME + value: '1' + - name: CREATE_FOLDERS + value: 'true' + - name: CREATE_TEMPLATE_DELTA_DISKS + value: 'true' + - name: CREATE_LINKED_CLONES + value: 'true' + - name: MAX_TRIES + value: '3' + - name: RETRY_FACTOR + value: '20' + - name: PURGE_UNCONFIGURED_FOLDERS + value: 'true' + - name: DOMAIN + value: {{ .Values.sut_domain }} + - name: VMPOOLER_CONFIG_FILE + value: /etc/vmpooler/vmpooler.yaml + - name: VMPOOLER_TRACING_ENABLED + value: '{{ .Values.tracing.enabled }}' + - name: VMPOOLER_TRACING_JAEGER_HOST + value: '{{ .Values.tracing.jaegerHost }}' + - name: LOGFILE + value: /dev/stdout + - name: EXTRA_CONFIG + value: '{{ .Values.additionalConfigFiles }}' + - name: TIMEOUT + value: '15' + - name: READY_TTL + value: "480" + - name: REDIS_SERVER + value: 'redis-master' + - name: REDIS_CONNECTION_POOL_SIZE + value: "200" + - name: REDIS_CONNECTION_POOL_TIMEOUT + value: "40" + - name: ONDEMAND_REQUEST_TTL + value: "50" + - name: ONDEMAND_CLONE_LIMIT + value: "50" + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: redis + key: redis-password + volumeMounts: + - name: base-config + mountPath: /etc/vmpooler + - name: pools-config + mountPath: /etc/vmpooler/config + volumes: + - name: base-config + configMap: + name: {{ .Values.configmapName }} + items: + - key: vmpooler.yaml + path: vmpooler.yaml + - name: pools-config + configMap: + name: {{ .Values.poolsConfigMapName }} + items: + - key: pools.yaml + path: pools.yaml + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm/vmpooler/templates/ingress.yaml b/helm/vmpooler/templates/ingress.yaml new file mode 100644 index 0000000..664f1d0 --- /dev/null +++ b/helm/vmpooler/templates/ingress.yaml @@ -0,0 +1,33 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "vmpooler.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "vmpooler.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + rules: + - host: '{{ .Values.vmpoolerInstance }}.{{ .Values.ingress.domain }}' + http: + paths: + - backend: + serviceName: {{ $fullName }}-api + servicePort: {{ $svcPort }} + path: / + {{ if .Values.ingress.useTLS -}} + tls: + - hosts: + - '{{ .Values.vmpoolerInstance }}.{{ .Values.ingress.domain }}' + secretName: '{{ .Values.vmpoolerInstance }}.{{ .Values.ingress.domain }}-tls' + {{- end }} +{{- end }} diff --git a/helm/vmpooler/templates/service-api.yaml b/helm/vmpooler/templates/service-api.yaml new file mode 100644 index 0000000..0e0f932 --- /dev/null +++ b/helm/vmpooler/templates/service-api.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "vmpooler.fullname" . }}-api + labels: + {{- include "vmpooler.labels" . | nindent 4 }} + annotations: + prometheus.io/scrape: {{ .Values.service.metricsEnabled | quote }} + prometheus.io/path: '/prometheus' +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.port }} + protocol: TCP + name: http + clusterIP: None + selector: + app: {{ include "vmpooler.fullname" . }}-api diff --git a/helm/vmpooler/templates/service-manager.yaml b/helm/vmpooler/templates/service-manager.yaml new file mode 100644 index 0000000..e2e3ad7 --- /dev/null +++ b/helm/vmpooler/templates/service-manager.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "vmpooler.fullname" . }}-manager + labels: + {{- include "vmpooler.labels" . | nindent 4 }} + annotations: + prometheus.io/scrape: {{ .Values.service.metricsEnabled | quote }} + prometheus.io/path: '/prometheus' +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.port }} + protocol: TCP + name: http + clusterIP: None + selector: + app: {{ include "vmpooler.fullname" . }}-manager diff --git a/helm/vmpooler/templates/serviceaccount.yaml b/helm/vmpooler/templates/serviceaccount.yaml new file mode 100644 index 0000000..2d5ac9c --- /dev/null +++ b/helm/vmpooler/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "vmpooler.serviceAccountName" . }} + labels: + {{- include "vmpooler.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/helm/vmpooler/templates/tests/test-connection.yaml b/helm/vmpooler/templates/tests/test-connection.yaml new file mode 100644 index 0000000..05ed423 --- /dev/null +++ b/helm/vmpooler/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "vmpooler.fullname" . }}-test-connection" + labels: + {{- include "vmpooler.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "vmpooler.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm/vmpooler/values.yaml b/helm/vmpooler/values.yaml new file mode 100644 index 0000000..dcfe191 --- /dev/null +++ b/helm/vmpooler/values.yaml @@ -0,0 +1,171 @@ +# this is used for the site name, the metrics prefix, and is +# the hostname in the ingress +vmpoolerInstance: 'test-instance' + +image: + repository: vmpooler + pullPolicy: Always + +configmapName: 'vmpooler-baseconfig' +sut_domain: 'vmpooler.example.com' +usage_stats: true + +tracing: + enabled: 'false' + # jaegerHost: 'http://otel-collector.otel-collector.svc:14268/api/traces' + +baseconfig_extras: {} + # :config: + # backend_weight: + # vmware_cluster1: 0 + # vmware_cluster2: 100 + # vmware_cluster4: 180 + +auth: + provider: 'dummy' + # provider: 'ldap' + # :ldap: + # host: 'ldap.example.com' + # port: 636 + # encryption: + # :method: :simple_tls + # :tls_options: + # :ssl_version: 'TLSv1_2' + # base: + # - 'ou=dept1,dc=example,dc=com' + # - 'ou=dept2,dc=example,dc=com' + # user_object: + # - 'uid' + # - 'cn' + +additionalConfigFiles: '/etc/vmpooler/config/pools.yaml' + +api: + replicaCount: 1 + resources: + requests: + cpu: 1500m + memory: 2Gi + limits: + cpu: 2000m + memory: 2Gi + +manager: + resources: + requests: + cpu: 4000m + memory: 4Gi + limits: + cpu: 4000m + memory: 4Gi + annotations: {} + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + # Letting the name be created automatically will prevent the SA resource from being + # shared between multiple instances of vmpooler such as when stage and prod are both + # deployed to the same cluster. + name: '' + +podSecurityContext: + fsGroup: 1001 + +securityContext: + capabilities: + drop: + - ALL + runAsNonRoot: true + readOnlyRootFilesystem: true + runAsUser: 1001 + runAsGroup: 1001 + +service: + type: ClusterIP + port: 4567 + metricsEnabled: true + +ingress: + enabled: false + # useTLS: true + # annotations: + # cert-manager.io/cluster-issuer: letsencrypt-prod + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: 'true' + # nginx.ingress.kubernetes.io/ssl-redirect: 'true' + # domain: k8s.example.com + +nodeSelector: {} + +tolerations: [] + +affinity: {} + +# Redis provided by bitnami/redis +# For a full list of parameters see: https://github.com/bitnami/charts/tree/master/bitnami/redis/ +redis: + fullnameOverride: redis + cluster: + enabled: false + networkPolicy: + enabled: true + securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + master: + resources: + requests: + cpu: 1000m + memory: 2048Mi + limits: + cpu: 1000m + memory: 2048Mi + livenessProbe: + failureThreshold: 10 + readinessProbe: + failureThreshold: 10 + metrics: + enabled: true + resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 256Mi + + +# Pools are listed last becaue the list can be really long when listed here. +# The default ConfigMap uses the value of the pools key to create pools.yaml. +# You could, instead, provide your own ConfigMap and specify its name below. +poolsConfigMapName: vmpooler-pools +pools: + - name: 'almalinux-8-x86_64-pixa4' + template: 'templates/cluster2/almalinux-8-x86_64-0.0.2' + folder: 'test-instance/cluster2/almalinux-8-x86_64' + datastore: 'vmpooler_cluster2' + clone_target: 'cluster2' + size: 5 + datacenter: 'pix' + provider: 'vsphere-ci67' + snapshot_mainMem_ioBlockPages: '2048' + snapshot_mainMem_iowait: '5' + network: 'vmpooler1' + - name: 'centos-6-x86_64-pixa4' + alias: [ 'centos-6-x86_64' ] + template: 'templates/cluster2/centos-6.8-x86_64-0.0.2-8gb' + folder: test-instance/cluster2/centos-6-x86_64' + datastore: 'vmpooler_cluster2' + clone_target: 'cluster2' + size: 0 + timeout: 5 + datacenter: 'pix' + provider: 'vsphere-ci67' + snapshot_mainMem_ioBlockPages: '2048' + snapshot_mainMem_iowait: '5' + network: 'vmpooler2' \ No newline at end of file