tpt-rails 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +127 -0
- data/Rakefile +32 -0
- data/app/assets/config/tpt_rails_manifest.js +1 -0
- data/app/assets/stylesheets/tpt/rails/application.css +15 -0
- data/app/controllers/tpt/rails/application_controller.rb +5 -0
- data/app/controllers/tpt/rails/error_tests_controller.rb +21 -0
- data/app/controllers/tpt/rails/health_checks_controller.rb +18 -0
- data/app/helpers/tpt/rails/application_helper.rb +4 -0
- data/app/jobs/tpt/rails/application_job.rb +4 -0
- data/app/mailers/tpt/rails/application_mailer.rb +6 -0
- data/app/models/tpt/rails/application_record.rb +5 -0
- data/app/views/layouts/tpt/rails/application.html.erb +15 -0
- data/config/routes.rb +6 -0
- data/lib/generators/tpt_rails/configuration/configuration_generator.rb +10 -0
- data/lib/generators/tpt_rails/configuration/templates/config/initializers/tpt_rails.rb +10 -0
- data/lib/generators/tpt_rails/continuous_integration/continuous_integration_generator.rb +17 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/Dockerfile.tt +31 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/docker-compose.yaml.tt +26 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/tasks/project.test.unit +12 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/test.Jenkinsfile.tt +40 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/test.config.xml.tt +76 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/tptcd.Jenkinsfile.tt +7 -0
- data/lib/generators/tpt_rails/continuous_integration/templates/ci/tptci.Jenkinsfile.tt +7 -0
- data/lib/generators/tpt_rails/kubernetes/kubernetes_generator.rb +156 -0
- data/lib/generators/tpt_rails/kubernetes/templates/Dockerfile.tt +41 -0
- data/lib/generators/tpt_rails/kubernetes/templates/entrypoint.sh +13 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/Chart.yaml.tt +16 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/README.md +142 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/requirements.yaml.tt +8 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/NOTES.txt.tt +8 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/_env.yaml +15 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/_helpers.tpl.tt +63 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/autoscaler.yaml.tt +21 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/deployment.yaml.tt +96 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/hooks/db-migrate.yaml.tt +59 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/templates/service.yaml.tt +52 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/values.prod.yaml.tt +52 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/values.staging.yaml.tt +52 -0
- data/lib/generators/tpt_rails/kubernetes/templates/kubernetes/helm/values.yaml.tt +171 -0
- data/lib/generators/tpt_rails/postman/postman.yaml.tt +2 -0
- data/lib/tasks/tpt/rails_tasks.rake +23 -0
- data/lib/tpt/rails.rb +65 -0
- data/lib/tpt/rails/config.rb +77 -0
- data/lib/tpt/rails/engine.rb +7 -0
- data/lib/tpt/rails/internal.rb +3 -0
- data/lib/tpt/rails/internal/datadog.rb +66 -0
- data/lib/tpt/rails/internal/error_reporter.rb +68 -0
- data/lib/tpt/rails/internal/health_checks.rb +32 -0
- data/lib/tpt/rails/version.rb +5 -0
- metadata +153 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
<?xml version='1.1' encoding='UTF-8'?><org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject plugin="workflow-multibranch@2.21">
|
2
|
+
<actions/>
|
3
|
+
<description></description>
|
4
|
+
<displayName><%= Tpt::Rails.app_name %>-test</displayName>
|
5
|
+
<properties>
|
6
|
+
<org.jenkinsci.plugins.workflow.libs.FolderLibraries plugin="workflow-cps-global-lib@2.13">
|
7
|
+
<libraries>
|
8
|
+
<org.jenkinsci.plugins.workflow.libs.LibraryConfiguration>
|
9
|
+
<name>jenkins-pipeline-library</name>
|
10
|
+
<retriever class="org.jenkinsci.plugins.workflow.libs.SCMSourceRetriever">
|
11
|
+
<scm class="jenkins.plugins.git.GitSCMSource" plugin="git@3.9.3">
|
12
|
+
<id>31643265-3634-6665-6262-343134326436</id>
|
13
|
+
<remote>git@github.com:TeachersPayTeachers/jenkins-pipeline-library.git</remote>
|
14
|
+
<credentialsId>jenkins-master-rsa-key</credentialsId>
|
15
|
+
<traits>
|
16
|
+
<jenkins.plugins.git.traits.BranchDiscoveryTrait/>
|
17
|
+
</traits>
|
18
|
+
</scm>
|
19
|
+
</retriever>
|
20
|
+
<implicit>false</implicit>
|
21
|
+
<allowVersionOverride>true</allowVersionOverride>
|
22
|
+
<includeInChangesets>true</includeInChangesets>
|
23
|
+
</org.jenkinsci.plugins.workflow.libs.LibraryConfiguration>
|
24
|
+
</libraries>
|
25
|
+
</org.jenkinsci.plugins.workflow.libs.FolderLibraries>
|
26
|
+
<org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig plugin="pipeline-model-definition@1.3.7">
|
27
|
+
<dockerLabel/>
|
28
|
+
<registry plugin="docker-commons@1.14"/>
|
29
|
+
</org.jenkinsci.plugins.pipeline.modeldefinition.config.FolderConfig>
|
30
|
+
</properties>
|
31
|
+
<folderViews class="jenkins.branch.MultiBranchProjectViewHolder" plugin="branch-api@2.4.0">
|
32
|
+
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
|
33
|
+
</folderViews>
|
34
|
+
<healthMetrics>
|
35
|
+
<com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric plugin="cloudbees-folder@6.8">
|
36
|
+
<nonRecursive>false</nonRecursive>
|
37
|
+
</com.cloudbees.hudson.plugins.folder.health.WorstChildHealthMetric>
|
38
|
+
<jenkins.branch.PrimaryBranchHealthMetric plugin="branch-api@2.4.0"/>
|
39
|
+
</healthMetrics>
|
40
|
+
<icon class="jenkins.branch.MetadataActionFolderIcon" plugin="branch-api@2.4.0">
|
41
|
+
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
|
42
|
+
</icon>
|
43
|
+
<orphanedItemStrategy class="com.cloudbees.hudson.plugins.folder.computed.DefaultOrphanedItemStrategy" plugin="cloudbees-folder@6.8">
|
44
|
+
<pruneDeadBranches>true</pruneDeadBranches>
|
45
|
+
<daysToKeep>-1</daysToKeep>
|
46
|
+
<numToKeep>5</numToKeep>
|
47
|
+
</orphanedItemStrategy>
|
48
|
+
<triggers/>
|
49
|
+
<disabled>false</disabled>
|
50
|
+
<sources class="jenkins.branch.MultiBranchProject$BranchSourceList" plugin="branch-api@2.4.0">
|
51
|
+
<data>
|
52
|
+
<jenkins.branch.BranchSource>
|
53
|
+
<source class="org.jenkinsci.plugins.github_branch_source.GitHubSCMSource" plugin="github-branch-source@2.4.5">
|
54
|
+
<id>65633436-6138-6163-6638-323663356636</id>
|
55
|
+
<credentialsId>tpt-github-source-credential</credentialsId>
|
56
|
+
<repoOwner>TeachersPayTeachers</repoOwner>
|
57
|
+
<repository><%= Tpt::Rails.app_name %></repository>
|
58
|
+
<traits>
|
59
|
+
<org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait>
|
60
|
+
<strategyId>3</strategyId>
|
61
|
+
</org.jenkinsci.plugins.github__branch__source.BranchDiscoveryTrait>
|
62
|
+
<org.jenkinsci.plugins.github__branch__source.TagDiscoveryTrait/>
|
63
|
+
</traits>
|
64
|
+
</source>
|
65
|
+
<strategy class="jenkins.branch.DefaultBranchPropertyStrategy">
|
66
|
+
<properties class="empty-list"/>
|
67
|
+
</strategy>
|
68
|
+
</jenkins.branch.BranchSource>
|
69
|
+
</data>
|
70
|
+
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
|
71
|
+
</sources>
|
72
|
+
<factory class="org.jenkinsci.plugins.workflow.multibranch.WorkflowBranchProjectFactory">
|
73
|
+
<owner class="org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject" reference="../.."/>
|
74
|
+
<scriptPath>ci/test.Jenkinsfile</scriptPath>
|
75
|
+
</factory>
|
76
|
+
</org.jenkinsci.plugins.workflow.multibranch.WorkflowMultiBranchProject>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
@Library('tpt-pipeline-library') _
|
2
|
+
|
3
|
+
def ci = new tpt.CI(this)
|
4
|
+
ci.project = '<%= Tpt::Rails.app_name %>'
|
5
|
+
ci.buildCredentials = [[ id: '<%= Tpt::Rails.app_name %>-rails-master-key', var: '<%= Tpt::Rails.app_name.underscore.upcase %>_RAILS_MASTER_KEY' ]]
|
6
|
+
|
7
|
+
tptcdPipeline(ci: ci)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
@Library('tpt-pipeline-library') _
|
2
|
+
|
3
|
+
def ci = new tpt.CI(this)
|
4
|
+
ci.project = '<%= Tpt::Rails.app_name %>'
|
5
|
+
ci.buildCredentials = [[ id: '<%= Tpt::Rails.app_name %>-rails-master-key', var: '<%= Tpt::Rails.app_name.underscore.upcase %>_RAILS_MASTER_KEY' ]]
|
6
|
+
|
7
|
+
tptciPipeline(ci: ci, ode: '') // we don't need an ode, just test this service
|
@@ -0,0 +1,156 @@
|
|
1
|
+
class Tpt::Rails::KubernetesGenerator < ::Rails::Generators::Base
|
2
|
+
VALID_APP_NAME = /^([a-z0-9]-?)+[a-z0-9]$/
|
3
|
+
ODE_KUBE_CONTEXT = 'kubernetes-eks-poc'
|
4
|
+
|
5
|
+
desc 'Set up the app with kubernetes'
|
6
|
+
|
7
|
+
source_root File.expand_path('templates', __dir__)
|
8
|
+
|
9
|
+
def setup_kubernetes
|
10
|
+
if !valid_app_name?
|
11
|
+
say("ERROR: App name #{Tpt::Rails.app_name.inspect} will not be valid for DNS usage.")
|
12
|
+
abort
|
13
|
+
end
|
14
|
+
|
15
|
+
check_dependencies
|
16
|
+
|
17
|
+
secrets_name = "#{Tpt::Rails.app_name}-secrets"
|
18
|
+
|
19
|
+
tpt_yaml = generate_tpt_yaml
|
20
|
+
team_name = tpt_yaml.fetch('owner')
|
21
|
+
app_type = tpt_yaml.fetch('target')
|
22
|
+
|
23
|
+
tpt_config = get_tpt_config
|
24
|
+
tpt_user_alias = tpt_config.fetch('user.alias')
|
25
|
+
|
26
|
+
default_email = get_default_email_address
|
27
|
+
email = ask("What's the maintainer email address? (#{default_email})").strip
|
28
|
+
email = email.presence || default_email || 'FIXME'
|
29
|
+
|
30
|
+
template 'Dockerfile'
|
31
|
+
template 'entrypoint.sh'
|
32
|
+
template '.dockerignore'
|
33
|
+
|
34
|
+
directory(
|
35
|
+
'kubernetes',
|
36
|
+
team_name: team_name,
|
37
|
+
secrets_name: secrets_name,
|
38
|
+
maintainer_email: email,
|
39
|
+
app_type: app_type,
|
40
|
+
)
|
41
|
+
|
42
|
+
inside('kubernetes/helm') do
|
43
|
+
run('helm dependency update', abort_on_failure: true)
|
44
|
+
end
|
45
|
+
|
46
|
+
add_kube_secret(secrets_name)
|
47
|
+
|
48
|
+
say(<<~MESSAGE)
|
49
|
+
|
50
|
+
********************************************************************************
|
51
|
+
* Success! Your project has been configured for Kubernetes!
|
52
|
+
********************************************************************************
|
53
|
+
|
54
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
55
|
+
! Follow-up actions required:
|
56
|
+
!
|
57
|
+
! 1. Review and commit these changes
|
58
|
+
! 2. Push your changes up to a Github repository
|
59
|
+
! 3. Run `tpt project check`
|
60
|
+
! 4. Deploy to an ODE with `tpt project push`
|
61
|
+
! 5. Give "Read-only" access to the Docker Hub "readonly" user:
|
62
|
+
! - Go to https://hub.docker.com/repository/docker/teacherspayteachers/#{Tpt::Rails.app_name}/permissions
|
63
|
+
! - Choose "readonly" in the "Team" dropdown box
|
64
|
+
! - Choose "Read-only" in the "Permission" box
|
65
|
+
! - Click the "+" button on the right-hand side
|
66
|
+
! 6. Monitor the status of your ODE deploy with:
|
67
|
+
! kubectl get pods --namespace #{tpt_user_alias} | grep #{Tpt::Rails.app_name}
|
68
|
+
! 7. When your ODE is ready you can visit it here:
|
69
|
+
! http://#{Tpt::Rails.app_name}--#{tpt_user_alias}.ode.tptpm.info/#{Tpt::Rails::Engine.instance.routes.url_helpers.health_check_path}
|
70
|
+
!
|
71
|
+
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
72
|
+
|
73
|
+
MESSAGE
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def check_dependencies
|
79
|
+
say('Checking dependencies…')
|
80
|
+
check_dependency('config/master.key', 'test -f config/master.key')
|
81
|
+
check_dependency('jq', 'which jq')
|
82
|
+
check_dependency('tpt network', 'nc -G1 -w1 chartmuseum.tptpm.info 443')
|
83
|
+
check_dependency('tpt-cli', 'tpt version')
|
84
|
+
check_dependency('tpt config', 'tpt config get') # fails if tpt config has not been initialized
|
85
|
+
check_dependency('docker', 'docker version') # fails if docker isn't running
|
86
|
+
check_dependency('kubectl', 'kubectl version') # fails if kubectl isn't set up
|
87
|
+
check_dependency('kubeval', 'which kubeval') # needed for `tpt project push`. See https://kubeval.instrumenta.dev/installation/
|
88
|
+
check_dependency('helm', 'helm search justtesting') # fails if helm isn't installed + initialized (~/.helm directory created)
|
89
|
+
check_dependency('helm tpt repo', "helm repo list | egrep '^tpt '") # fails if tpt helm repo isn't added
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_dependency(name, command)
|
93
|
+
say("Checking for dependency: #{name}")
|
94
|
+
run(command, abort_on_failure: true, capture: true) # capture: true makes the output nicer
|
95
|
+
rescue Exception => e
|
96
|
+
say "Error! #{name} does not seem to be available. `#{command}` returned: #{e.inspect}"
|
97
|
+
abort
|
98
|
+
end
|
99
|
+
|
100
|
+
# Dasherize the app name for kube usage, and verify that it will be valid for DNS usage.
|
101
|
+
# Note: Rails itself also verifies that the name does not start with a number so that it is a valid
|
102
|
+
# Ruby identifier.
|
103
|
+
def valid_app_name?
|
104
|
+
Tpt::Rails.app_name =~ VALID_APP_NAME
|
105
|
+
end
|
106
|
+
|
107
|
+
def generate_tpt_yaml
|
108
|
+
# TODO: Add an options to `tpt project init` that allows passing in these values
|
109
|
+
say(<<~TEXT)
|
110
|
+
|
111
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
112
|
+
! IMPORTANT !
|
113
|
+
! In the prompt below, please enter "#{Tpt::Rails.app_name}" for the "project name"
|
114
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
115
|
+
|
116
|
+
TEXT
|
117
|
+
|
118
|
+
run('tpt project init --force', abort_on_failure: true)
|
119
|
+
|
120
|
+
YAML.load(run('cat tpt.yaml', capture: true, abort_on_failure: true))
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_tpt_config
|
124
|
+
conf = run('tpt config get', capture: true, abort_on_failure: true)
|
125
|
+
YAML.load(conf)
|
126
|
+
end
|
127
|
+
|
128
|
+
def get_default_email_address
|
129
|
+
run('git config user.email', capture: true, abort_on_failure: false)&.strip.presence
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_kube_secret(secrets_name)
|
133
|
+
secret = run('cat config/master.key', capture: true, abort_on_failure: true).strip
|
134
|
+
key_exists = run("kubectl get secret #{secrets_name} --context='#{ODE_KUBE_CONTEXT}' >/dev/null 2>&1", abort_on_failure: false)
|
135
|
+
|
136
|
+
if key_exists
|
137
|
+
say "Kube secret #{secrets_name} already exists. Updating it…"
|
138
|
+
ENV['KUBE_GENERATOR_RAILS_MASTER_KEY'] = Base64.strict_encode64(secret)
|
139
|
+
run(
|
140
|
+
<<~CMD,
|
141
|
+
kubectl get secret #{secrets_name} --context=#{ODE_KUBE_CONTEXT} --output=json \
|
142
|
+
| jq --arg key $KUBE_GENERATOR_RAILS_MASTER_KEY '.data["rails-master-key"]=$key' \
|
143
|
+
| kubectl apply --context=#{ODE_KUBE_CONTEXT} -f -
|
144
|
+
CMD
|
145
|
+
abort_on_failure: true,
|
146
|
+
)
|
147
|
+
else
|
148
|
+
say "Kube secret #{secrets_name} not found. Creating it…"
|
149
|
+
run(
|
150
|
+
"kubectl create secret generic #{secrets_name} --from-literal='rails-master-key=#{secret}' --context='#{ODE_KUBE_CONTEXT}'",
|
151
|
+
abort_on_failure: true,
|
152
|
+
)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
FROM ruby:<%= RUBY_VERSION %>
|
2
|
+
|
3
|
+
RUN mkdir /app
|
4
|
+
WORKDIR /app
|
5
|
+
|
6
|
+
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash \
|
7
|
+
&& apt-get update && apt-get install -y nodejs && rm -rf /var/lib/apt/lists/* \
|
8
|
+
&& curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
|
9
|
+
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
|
10
|
+
&& apt-get update && apt-get install -y yarn && rm -rf /var/lib/apt/lists/*
|
11
|
+
|
12
|
+
COPY package.json yarn.lock /app/
|
13
|
+
RUN yarn install --check-files
|
14
|
+
|
15
|
+
# Set production env for asset compilation
|
16
|
+
ARG RAILS_ENV=production
|
17
|
+
ARG APP_ENV=production
|
18
|
+
ARG APP_NAME=<%= Tpt::Rails.app_name %>
|
19
|
+
|
20
|
+
ARG <%= Tpt::Rails.app_name.underscore.upcase %>_RAILS_MASTER_KEY
|
21
|
+
# GITHUB_TOKEN allows accessing our private tpt-rails gem on Github
|
22
|
+
ARG GITHUB_TOKEN
|
23
|
+
|
24
|
+
COPY Gemfile /app/Gemfile
|
25
|
+
COPY Gemfile.lock /app/Gemfile.lock
|
26
|
+
RUN gem update bundler
|
27
|
+
RUN BUNDLE_GITHUB__COM="${GITHUB_TOKEN}:x-oauth-basic" bundle install --without development test
|
28
|
+
|
29
|
+
# Add a script to be executed every time the container starts.
|
30
|
+
COPY entrypoint.sh /usr/bin/
|
31
|
+
RUN chmod +x /usr/bin/entrypoint.sh
|
32
|
+
|
33
|
+
ADD . /app
|
34
|
+
|
35
|
+
RUN RAILS_MASTER_KEY=${<%= Tpt::Rails.app_name.underscore.upcase %>_RAILS_MASTER_KEY} bin/rails assets:precompile
|
36
|
+
|
37
|
+
EXPOSE 3000
|
38
|
+
|
39
|
+
# TODO: Delete this?
|
40
|
+
ENTRYPOINT ["entrypoint.sh"]
|
41
|
+
CMD ["rails", "server", "-b", "0.0.0.0"]
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
if [ -f /app/tmp/pids/server.pid ]; then
|
5
|
+
rm /app/tmp/pids/server.pid
|
6
|
+
fi
|
7
|
+
|
8
|
+
echo "Preparing database"
|
9
|
+
# In Kubernetes this should always be a no-op because our migration hook will already have run.
|
10
|
+
rails db:prepare
|
11
|
+
|
12
|
+
echo "Starting server"
|
13
|
+
exec "$@"
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# This is the entrypoint to the Helm Chart
|
2
|
+
# Most properties here are pretty self explanatory
|
3
|
+
|
4
|
+
name: <%= Tpt::Rails.app_name %>
|
5
|
+
# Chart version - increase this whenever you change anything in the helm/ folder. tpt-cli will warn
|
6
|
+
# you if you don't.
|
7
|
+
version: 0.0.1
|
8
|
+
# Application version - can be useful if we run multiple versions of the app at the same time.
|
9
|
+
appVersion: 0.0.1
|
10
|
+
description: <%= Tpt::Rails.app_name %> service
|
11
|
+
keywords: [odev2, rails, service-template, '<%= Tpt::Rails.app_name %>', '<%= config.fetch(:app_type) %>']
|
12
|
+
home: https://www.teacherspayteachers.com
|
13
|
+
icon: https://static1.teacherspayteachers.com/tpt-frontend/releases/production/current/a24f03b12028dbc93cf182318e6993e5.svg
|
14
|
+
sources: ['https://github.com/TeachersPayTeachers/<%= Tpt::Rails.app_name %>']
|
15
|
+
maintainers: [{name: '<%= config.fetch(:team_name) %>', email: '<%= config.fetch(:maintainer_email) %>'}]
|
16
|
+
engine: gotpl # yaml parsing engine, just leave this alone
|
@@ -0,0 +1,142 @@
|
|
1
|
+
# Example Helm Chart
|
2
|
+
|
3
|
+
This is an example [Helm Chart](https://helm.sh/docs/developing_charts/).
|
4
|
+
Helm sits a layer above Kubernetes, and provides a simpler interface for defining Kubernetes entities across environments. It also provides a mechanism for declaring dependencies between Kubernetes services, although we're not currently using that feature. You can think of Helm as a package manager and deployment tool for Kubernetes entities.
|
5
|
+
|
6
|
+
Currently, we use Helm to manage deployments and updates to our On Demand Environments (ODEs). Production deploys use Kubernetes directly, except for graphapi which is also using Helm.
|
7
|
+
|
8
|
+
If you'd like your service to deploy to an ODE (you should!), you'll need to build a Chart for it and reference that chart from our [Charts repo](https://github.com/TeachersPayTeachers/charts). See [this branch](https://github.com/TeachersPayTeachers/charts/compare/adding-service-example) for a reference on how to do this.
|
9
|
+
|
10
|
+
## General Chart Overview
|
11
|
+
|
12
|
+
There are a couple files that constitute a Chart. Here is an overview:
|
13
|
+
|
14
|
+
```
|
15
|
+
(Project Root)
|
16
|
+
└── kubernetes
|
17
|
+
└── helm
|
18
|
+
├── charts
|
19
|
+
│ ├── copy-secrets-1.1.5.tgz
|
20
|
+
├── templates
|
21
|
+
│ ├── _helpers.tpl
|
22
|
+
│ ├── autoscaler.yaml
|
23
|
+
│ ├── deployment.yaml
|
24
|
+
│ ├── NOTES.txt
|
25
|
+
│ └── service.yaml
|
26
|
+
├── .helmignore
|
27
|
+
├── Chart.yaml
|
28
|
+
├── README.md
|
29
|
+
├── requirements.lock
|
30
|
+
├── requirements.yaml
|
31
|
+
├── values.dev.yaml
|
32
|
+
├── values.staging.yaml
|
33
|
+
└── values.yaml
|
34
|
+
```
|
35
|
+
|
36
|
+
### Chart.yaml
|
37
|
+
|
38
|
+
This is Helm's entrypoint to the Chart. Here the high level metadata is specified, including the Chart name and description.
|
39
|
+
|
40
|
+
### values.yaml
|
41
|
+
|
42
|
+
Here you can configure any variables that your Chart will depend on. This includes properties like application secrets, and endpoint configuration of service dependencies. These variables can then be referenced in the Chart definition, to construct your Kubernetes entity.
|
43
|
+
|
44
|
+
Usually, you want different configurations for different deployment environments. We use `values.yaml` to configure a standard production deployment, and then use `values.{env}.yaml` to override values where a given environment differs from production. In this service, we have created `values.dev.yaml` for configuring ODE deployments and `values.staging.yaml` to configure deployments to staging.
|
45
|
+
|
46
|
+
### Secrets
|
47
|
+
|
48
|
+
See [this wiki page](https://teacherspayteachers.atlassian.net/wiki/spaces/ENGINEERING/pages/64946180/Using+Secrets+in+Kubernetes) for information on how to define a secret in Kubernetes. After adding the secret to the cluster, you can reference it in a `values` file which will cause it to be passed to your application as an environmental variable:
|
49
|
+
|
50
|
+
```yaml
|
51
|
+
secrets:
|
52
|
+
- name: BUGSNAG_API_KEY
|
53
|
+
valueFrom:
|
54
|
+
secretKeyRef:
|
55
|
+
name: service-template-node-bugsnag
|
56
|
+
key: apiKey
|
57
|
+
```
|
58
|
+
|
59
|
+
If you wish to use a secret in ODEs environnments as well, will need to use the `copy-secrets` dependency chart so that the secrets are copied from the default namespace when a user spins up an ODE. See the [documentation for the chart](https://github.com/TeachersPayTeachers/charts/tree/master/copy-secrets) for more information. A service which needed a secret called `super-secret` when running in ODEs would have something like this in its `values.dev.yaml` file:
|
60
|
+
|
61
|
+
```yaml
|
62
|
+
secrets:
|
63
|
+
- name: SUPER_SECRET
|
64
|
+
valueFrom:
|
65
|
+
secretKeyRef:
|
66
|
+
name: super-secret
|
67
|
+
key: key
|
68
|
+
|
69
|
+
copy-secrets:
|
70
|
+
secrets:
|
71
|
+
- super-secret
|
72
|
+
```
|
73
|
+
|
74
|
+
### /templates
|
75
|
+
|
76
|
+
Helm will process all files in this directory, and combine them into a Kubernetes entity definition. These files might look like the Kube deployment specifications you're used to (e.g. for [tpt-frontend](https://github.com/TeachersPayTeachers/tpt-frontend/blob/master/kubernetes/deployment.production.yaml)), with one major difference: they can reference variables. These variables can come from a couple places, described in more detail below.
|
77
|
+
|
78
|
+
### requirements.yaml
|
79
|
+
|
80
|
+
This optional file lists dependencies for your chart. After adding or changing an entry in this file, you can download all dependencies into the `charts` folder and update `requirements.lock` with the command `helm dependency update`. Right now, the only dependency included is the `copy-secrets` chart mentioned above.
|
81
|
+
|
82
|
+
## Template file syntax
|
83
|
+
|
84
|
+
The meat of the Helm Chart is the `/templates` directory, which contains all the resources needed for Helm to construct and deploy a Kubernetes resource. These files mostly look like Kubernetes entity definitions, but with reference to variables. You can find documentation on what's available [here](https://helm.sh/docs/chart_template_guide/#built-in-objects), but below are couple common patterns you'll see.
|
85
|
+
|
86
|
+
### myProperty: {{ .Values.foo.bar }}
|
87
|
+
|
88
|
+
Inserts `someValue` as the value of myProperty, where `someValue` was defined in your `values.yaml` file:
|
89
|
+
|
90
|
+
```yaml
|
91
|
+
foo:
|
92
|
+
bar: someValue
|
93
|
+
```
|
94
|
+
|
95
|
+
### myProperty: {{ template "foo.bar" }}
|
96
|
+
|
97
|
+
References a property that was specified by `_helpers.tpl`
|
98
|
+
|
99
|
+
```
|
100
|
+
{{- define "foo.bar" -}}
|
101
|
+
{{- if .Values.fullnameOverride -}}
|
102
|
+
{{- .Values.fullnameOverride -}}
|
103
|
+
{{- else -}}
|
104
|
+
{{- .Values.nameOverride -}}
|
105
|
+
{{- end -}}
|
106
|
+
{{- end -}}
|
107
|
+
```
|
108
|
+
|
109
|
+
This is a good way of bringing in variables that need a bit of processing to transform them from raw `values.yaml` properties.
|
110
|
+
|
111
|
+
### myProperty: "{{ .Release.Name }}"
|
112
|
+
|
113
|
+
References Helm defined variables
|
114
|
+
|
115
|
+
## Testing Your Helm Charts
|
116
|
+
|
117
|
+
If this prints the contents of your helm charts without erroring, there are no syntax errors.
|
118
|
+
|
119
|
+
`helm install --dry-run --debug </path/to/helm/chart/dir>`
|
120
|
+
|
121
|
+
The TpT-CLI tool should be used to ensure your service will work with TpT ODEs v2.
|
122
|
+
|
123
|
+
[TpT-CLI Github Repo](https://github.com/TeachersPayTeachers/tpt-cli)
|
124
|
+
|
125
|
+
[ODE v2 Reference](https://teacherspayteachers.atlassian.net/wiki/spaces/ODE/pages/586678286/IN+PROGRESS+Getting+Started+with+ODE+Evolution+v+2.0+-+TpT-CLI)
|
126
|
+
|
127
|
+
## Testing Your Service
|
128
|
+
### Contract and API Functional Testing (IN PROGRESS)
|
129
|
+
Helm test charts can be used to test your service in isolation. If you have tests defined in Postman (SUPPORTED) or your service repo (IN PROGRESS), you can execute them using the TpT-CLI test command (IN PROGRESS).
|
130
|
+
|
131
|
+
**_Do not use `helm test` directly to run tests. This requires some extra coordination to not leave test pods running and still have access to test results. TpT-CLI will be updated with a tet command to handle this orchestration._**
|
132
|
+
|
133
|
+
#### Usage
|
134
|
+
1) Create a helm test chart under [templates/tests](templates/tests). See [test.apiFunctional.postman.yaml](templates/tests/test.apiFunctional.postman.yaml)
|
135
|
+
2) Add any test configuration to the appropriate values.yaml.
|
136
|
+
3) Verify your changes using `tpt project check`.
|
137
|
+
4) Commit your changes.
|
138
|
+
5) Release your charts using `tpt project push`.
|
139
|
+
6) (IN PROGRESS) Execute the tests via the TPT-CLI test command.
|
140
|
+
|
141
|
+
### Example Postman Tests
|
142
|
+
https://tptapis.postman.co/collections/5817358-93d711ed-3181-480d-bba5-ab68b0a29eae?version=latest&workspace=d9a6b5ae-1a22-4d0b-b706-2cc818031f79
|