├── .browserslistrc
├── .github
└── workflows
│ └── deploy.yaml
├── .gitignore
├── .pryrc
├── .psqlrc
├── Aptfile
├── Dockerfile
├── Dockerfile.prod
├── Gemfile
├── Gemfile.lock
├── Makefile
├── README.md
├── Rakefile
├── app
├── assets
│ ├── config
│ │ └── manifest.js
│ ├── images
│ │ ├── .keep
│ │ ├── do-logo.png
│ │ └── wagon-logo.png
│ └── stylesheets
│ │ └── application.css
├── channels
│ ├── application_cable
│ │ ├── channel.rb
│ │ └── connection.rb
│ └── messages_channel.rb
├── controllers
│ ├── application_controller.rb
│ ├── concerns
│ │ └── .keep
│ ├── logins_controller.rb
│ └── messages_controller.rb
├── helpers
│ ├── application_helper.rb
│ ├── login_helper.rb
│ └── messages_helper.rb
├── javascript
│ ├── channels
│ │ ├── consumer.js
│ │ └── index.js
│ ├── controllers
│ │ ├── chat_controller.js
│ │ └── index.js
│ ├── packs
│ │ └── application.js
│ └── stylesheets
│ │ ├── application.scss
│ │ └── chat.scss
├── jobs
│ ├── application_job.rb
│ └── deliver_message_job.rb
├── mailers
│ └── application_mailer.rb
├── models
│ ├── application_record.rb
│ ├── concerns
│ │ └── .keep
│ ├── current.rb
│ ├── message.rb
│ └── preview.rb
├── services
│ ├── message_broadcaster.rb
│ ├── preview_broadcaster.rb
│ └── preview_creator.rb
└── views
│ ├── layouts
│ ├── application.html.erb
│ ├── mailer.html.erb
│ └── mailer.text.erb
│ ├── logins
│ └── new.html.erb
│ ├── messages
│ ├── _message.html.erb
│ └── index.html.erb
│ └── previews
│ └── _preview.html.erb
├── babel.config.js
├── bin
├── bundle
├── rails
├── rake
├── setup
├── spring
├── webpack
├── webpack-dev-server
└── yarn
├── config.ru
├── config
├── application.rb
├── boot.rb
├── cable.yml
├── credentials.yml.enc
├── database.yml
├── environment.rb
├── environments
│ ├── development.rb
│ ├── production.rb
│ └── test.rb
├── initializers
│ ├── application_controller_renderer.rb
│ ├── assets.rb
│ ├── backtrace_silencers.rb
│ ├── content_security_policy.rb
│ ├── cookies_serializer.rb
│ ├── filter_parameter_logging.rb
│ ├── inflections.rb
│ ├── mime_types.rb
│ ├── sidekiq_redis.rb
│ └── wrap_parameters.rb
├── locales
│ └── en.yml
├── puma.rb
├── routes.rb
├── sidekiq.yml
├── spring.rb
├── webpack
│ ├── development.js
│ ├── environment.js
│ ├── production.js
│ └── test.js
└── webpacker.yml
├── db
├── migrate
│ ├── 20191126104811_create_messages.rb
│ └── 20191203142803_create_previews.rb
├── schema.rb
└── seeds.rb
├── dip.yml
├── docker-compose.yml
├── docs
├── app.png
├── processes.png
└── secrets.png
├── helm
├── Chart.lock
├── Chart.yaml
├── charts
│ └── redis-10.2.1.tgz
├── templates
│ ├── NOTES.txt
│ ├── _helpers.tpl
│ ├── deployments
│ │ ├── rails.yaml
│ │ └── sidekiq.yaml
│ ├── environment
│ │ └── common-env.yaml
│ ├── ingress.yaml
│ ├── jobs
│ │ └── db-prepare.yaml
│ ├── secrets
│ │ ├── db-connection-secret.yaml
│ │ └── master-key-secret.yaml
│ ├── service.yaml
│ ├── serviceaccount.yaml
│ ├── ssl
│ │ ├── cert.yaml
│ │ └── production-issuer.yaml
│ └── tests
│ │ └── test-connection.yaml
└── values.yaml
├── lib
├── assets
│ └── .keep
└── tasks
│ └── .keep
├── log
└── .keep
├── package.json
├── postcss.config.js
├── public
├── 404.html
├── 422.html
├── 500.html
├── apple-touch-icon-precomposed.png
├── apple-touch-icon.png
├── favicon.ico
└── robots.txt
├── test
├── application_system_test_case.rb
├── channels
│ ├── application_cable
│ │ └── connection_test.rb
│ └── messages_channel_test.rb
├── controllers
│ ├── .keep
│ ├── logins_controller_test.rb
│ └── messages_controller_test.rb
├── fixtures
│ ├── .keep
│ └── files
│ │ └── .keep
├── helpers
│ └── .keep
├── integration
│ └── .keep
├── mailers
│ └── .keep
├── models
│ ├── .keep
│ ├── message_test.rb
│ └── preview_test.rb
├── system
│ └── .keep
└── test_helper.rb
├── tmp
└── .keep
├── vendor
└── .keep
└── yarn.lock
/.browserslistrc:
--------------------------------------------------------------------------------
1 | defaults
2 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Helm deploy
2 | on:
3 | push:
4 | branches:
5 | - master
6 |
7 | jobs:
8 | deploy:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@master
12 |
13 | - name: Login to Quay
14 | run: docker login quay.io -u ${{ secrets.QUAY_USERNAME }} -p "${{ secrets.QUAY_PASSWORD }}"
15 |
16 | - name: Pull latest image to use for cache
17 | run: docker pull quay.io/lewagon/rails-k8s-demo:latest || true
18 |
19 | - name: Copy master key from a secret
20 | run: echo ${{ secrets.RAILS_MASTER_KEY }} > config/master.key
21 |
22 | - name: Build and push prod image
23 | run: make build-ci
24 |
25 | - name: Install doctl
26 | uses: digitalocean/action-doctl@v2
27 | with:
28 | token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
29 |
30 | - name: Save DigitalOcean kubeconfig
31 | run: doctl kubernetes cluster kubeconfig show kubernetes-demo > $GITHUB_WORKSPACE/.kubeconfig
32 |
33 | - name: Install/upgrade chart
34 | run: >
35 | export KUBECONFIG=$GITHUB_WORKSPACE/.kubeconfig &&
36 | make ci-deploy latest_sha=$(echo $GITHUB_SHA | head -c7)
37 | db_connection_string=${{ secrets.DB_URL }} rails_master_key=${{secrets.RAILS_MASTER_KEY}}
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2 | #
3 | # If you find yourself ignoring temporary files generated by your text editor
4 | # or operating system, you probably want to add a global ignore instead:
5 | # git config --global core.excludesfile '~/.gitignore_global'
6 |
7 | # Ignore bundler config.
8 | /.bundle
9 |
10 | # Ignore all logfiles and tempfiles.
11 | /log/*
12 | /tmp/*
13 | !/log/.keep
14 | !/tmp/.keep
15 |
16 |
17 | /public/assets
18 | .byebug_history
19 |
20 | # Ignore master key for decrypting credentials and more.
21 | /config/master.key
22 |
23 | /public/packs
24 | /public/packs-test
25 | /node_modules
26 | /yarn-error.log
27 | yarn-debug.log*
28 | .yarn-integrity
29 | .pry_history
30 | .ruby-version
31 |
--------------------------------------------------------------------------------
/.pryrc:
--------------------------------------------------------------------------------
1 | Pry.config.history.should_save = true
2 | Pry.config.history.file = File.join(__dir__, ".pry_history")
3 |
--------------------------------------------------------------------------------
/.psqlrc:
--------------------------------------------------------------------------------
1 | \set HISTFILE `[[ -z $PSQL_HISTFILE ]] && echo $HOME/.psql_history || echo $PSQL_HISTFILE`
2 |
--------------------------------------------------------------------------------
/Aptfile:
--------------------------------------------------------------------------------
1 | vim
2 | libidn11-dev
3 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # https://github.com/lewagon/rails-base-chrome-imagemagick/tree/dev
2 | FROM quay.io/lewagon/rails-base-chrome-imagemagick:dev
3 | ARG BUNDLER_VERSION
4 | ENV BUNDLER_VERSION=${BUNDLER_VERSION:-2.1.4}
5 |
6 | COPY ./Aptfile /tmp/Aptfile
7 | RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
8 | DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
9 | $(cat /tmp/Aptfile | xargs) && \
10 | apt-get clean && \
11 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
12 | truncate -s 0 /var/log/*log
13 |
14 | # Configure bundler
15 | ENV LANG=C.UTF-8 \
16 | BUNDLE_JOBS=4 \
17 | BUNDLE_RETRY=3
18 |
19 | ENV PATH /app/bin:$PATH
20 |
21 | WORKDIR /app
22 |
23 | # Upgrade RubyGems and install required Bundler version
24 | RUN gem update --system && \
25 | gem install bundler -v $BUNDLER_VERSION
--------------------------------------------------------------------------------
/Dockerfile.prod:
--------------------------------------------------------------------------------
1 | # https://github.com/lewagon/rails-base-chrome-imagemagick/tree/dev
2 | FROM quay.io/lewagon/rails-base-chrome-imagemagick:dev
3 | ARG BUNDLER_VERSION
4 | ENV BUNDLER_VERSION=${BUNDLER_VERSION:-2.1.4}
5 |
6 | COPY ./Aptfile /tmp/Aptfile
7 | RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade && \
8 | DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends \
9 | $(cat /tmp/Aptfile | xargs) && \
10 | apt-get clean && \
11 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
12 | truncate -s 0 /var/log/*log
13 |
14 | # Configure bundler
15 | ENV LANG=C.UTF-8 \
16 | BUNDLE_JOBS=4 \
17 | BUNDLE_RETRY=3
18 |
19 | ENV PATH /app/bin:$PATH
20 |
21 | WORKDIR /app
22 |
23 | COPY Gemfile Gemfile.lock package.json yarn.lock ./
24 |
25 | # Upgrade RubyGems and install required Bundler version
26 | RUN gem update --system && \
27 | gem install bundler:$BUNDLER_VERSION && \
28 | bundle config set deployment 'true' && \
29 | bundle config set without 'development test' && \
30 | bundle install
31 |
32 | COPY app/ ./app
33 | COPY bin/ ./bin
34 | COPY config/ ./config
35 | COPY lib/ ./lib
36 | COPY db/ ./db
37 | COPY public/ ./public
38 | COPY config.ru Rakefile postcss.config.js babel.config.js ./
39 | RUN mkdir log
40 |
41 |
42 | ENV RAILS_ENV=production
43 | ENV NODE_ENV=production
44 |
45 | # Hack to not leak the master_key for build
46 | # https://github.com/rails/rails/issues/32947#issuecomment-531508722
47 | ENV ASSETS_PRECOMPILE=1
48 | ENV SECRET_KEY_BASE=1
49 |
50 | RUN rails assets:precompile
51 |
52 | ENV RAILS_LOG_TO_STDOUT=enabled
53 | ENV RAILS_SERVE_STATIC_FILES=enabled
54 |
55 | # cleanup
56 | RUN rm -rf node_modules tmp/cache vendor/assets test
57 |
58 | EXPOSE 3000
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source "https://rubygems.org"
2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3 |
4 | ruby "2.6.6"
5 |
6 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
7 | gem "rails", "~> 6.0.3.1"
8 | # Use postgresql as the database for Active Record
9 | gem "pg", ">= 0.18", "< 2.0"
10 | # Use Puma as the app server
11 | gem "puma", "~> 3.12"
12 | # Use SCSS for stylesheets
13 | gem "sass-rails", "~> 6"
14 | # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker
15 | gem "webpacker", "~> 4.0"
16 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
17 | gem "turbolinks", "~> 5"
18 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
19 | gem "jbuilder", "~> 2.7"
20 | # Use Redis adapter to run Action Cable in production
21 | gem "redis", "~> 4.0"
22 | gem "sidekiq", "~> 6.0"
23 | gem "sidekiq-failures"
24 | gem "sidekiq_alive", "~> 2.0" # liveness probe for Sidekiq pod in Kubernetes
25 |
26 | gem "http"
27 | gem "twitter-text"
28 | gem "opengraph_parser"
29 | # Use Active Model has_secure_password
30 | # gem 'bcrypt', '~> 3.1.7'
31 |
32 | # Reduces boot times through caching; required in config/boot.rb
33 | gem "bootsnap", ">= 1.4.2", require: false
34 |
35 | group :development, :test do
36 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console
37 | gem "byebug", platforms: [:mri, :mingw, :x64_mingw]
38 | end
39 |
40 | group :development do
41 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code.
42 | gem "web-console", ">= 3.3.0"
43 | gem "listen", ">= 3.0.5", "< 3.2"
44 | gem "httplog"
45 | gem "rspec", "~> 3.9"
46 | gem "pry-rails"
47 | end
48 |
49 | group :test do
50 | # Adds support for Capybara system testing and selenium driver
51 | gem "capybara", ">= 2.15"
52 | gem "selenium-webdriver"
53 | # Easy installation and use of web drivers to run system tests with browsers
54 | gem "webdrivers"
55 | end
56 |
57 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
58 | gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby]
59 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | actioncable (6.0.3.2)
5 | actionpack (= 6.0.3.2)
6 | nio4r (~> 2.0)
7 | websocket-driver (>= 0.6.1)
8 | actionmailbox (6.0.3.2)
9 | actionpack (= 6.0.3.2)
10 | activejob (= 6.0.3.2)
11 | activerecord (= 6.0.3.2)
12 | activestorage (= 6.0.3.2)
13 | activesupport (= 6.0.3.2)
14 | mail (>= 2.7.1)
15 | actionmailer (6.0.3.2)
16 | actionpack (= 6.0.3.2)
17 | actionview (= 6.0.3.2)
18 | activejob (= 6.0.3.2)
19 | mail (~> 2.5, >= 2.5.4)
20 | rails-dom-testing (~> 2.0)
21 | actionpack (6.0.3.2)
22 | actionview (= 6.0.3.2)
23 | activesupport (= 6.0.3.2)
24 | rack (~> 2.0, >= 2.0.8)
25 | rack-test (>= 0.6.3)
26 | rails-dom-testing (~> 2.0)
27 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
28 | actiontext (6.0.3.2)
29 | actionpack (= 6.0.3.2)
30 | activerecord (= 6.0.3.2)
31 | activestorage (= 6.0.3.2)
32 | activesupport (= 6.0.3.2)
33 | nokogiri (>= 1.8.5)
34 | actionview (6.0.3.2)
35 | activesupport (= 6.0.3.2)
36 | builder (~> 3.1)
37 | erubi (~> 1.4)
38 | rails-dom-testing (~> 2.0)
39 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
40 | activejob (6.0.3.2)
41 | activesupport (= 6.0.3.2)
42 | globalid (>= 0.3.6)
43 | activemodel (6.0.3.2)
44 | activesupport (= 6.0.3.2)
45 | activerecord (6.0.3.2)
46 | activemodel (= 6.0.3.2)
47 | activesupport (= 6.0.3.2)
48 | activestorage (6.0.3.2)
49 | actionpack (= 6.0.3.2)
50 | activejob (= 6.0.3.2)
51 | activerecord (= 6.0.3.2)
52 | marcel (~> 0.3.1)
53 | activesupport (6.0.3.2)
54 | concurrent-ruby (~> 1.0, >= 1.0.2)
55 | i18n (>= 0.7, < 2)
56 | minitest (~> 5.1)
57 | tzinfo (~> 1.1)
58 | zeitwerk (~> 2.2, >= 2.2.2)
59 | addressable (2.7.0)
60 | public_suffix (>= 2.0.2, < 5.0)
61 | bindex (0.8.1)
62 | bootsnap (1.4.6)
63 | msgpack (~> 1.0)
64 | builder (3.2.4)
65 | byebug (11.1.3)
66 | capybara (3.32.2)
67 | addressable
68 | mini_mime (>= 0.1.3)
69 | nokogiri (~> 1.8)
70 | rack (>= 1.6.0)
71 | rack-test (>= 0.6.3)
72 | regexp_parser (~> 1.5)
73 | xpath (~> 3.2)
74 | childprocess (3.0.0)
75 | coderay (1.1.3)
76 | concurrent-ruby (1.1.6)
77 | connection_pool (2.2.3)
78 | crass (1.0.6)
79 | diff-lcs (1.3)
80 | domain_name (0.5.20190701)
81 | unf (>= 0.0.5, < 1.0.0)
82 | erubi (1.9.0)
83 | ffi (1.13.1)
84 | ffi-compiler (1.0.1)
85 | ffi (>= 1.0.0)
86 | rake
87 | globalid (0.4.2)
88 | activesupport (>= 4.2.0)
89 | http (4.4.1)
90 | addressable (~> 2.3)
91 | http-cookie (~> 1.0)
92 | http-form_data (~> 2.2)
93 | http-parser (~> 1.2.0)
94 | http-cookie (1.0.3)
95 | domain_name (~> 0.5)
96 | http-form_data (2.3.0)
97 | http-parser (1.2.1)
98 | ffi-compiler (>= 1.0, < 2.0)
99 | httplog (1.4.3)
100 | rack (>= 1.0)
101 | rainbow (>= 2.0.0)
102 | i18n (1.8.3)
103 | concurrent-ruby (~> 1.0)
104 | idn-ruby (0.1.0)
105 | jbuilder (2.10.0)
106 | activesupport (>= 5.0.0)
107 | listen (3.1.5)
108 | rb-fsevent (~> 0.9, >= 0.9.4)
109 | rb-inotify (~> 0.9, >= 0.9.7)
110 | ruby_dep (~> 1.2)
111 | loofah (2.6.0)
112 | crass (~> 1.0.2)
113 | nokogiri (>= 1.5.9)
114 | mail (2.7.1)
115 | mini_mime (>= 0.1.1)
116 | marcel (0.3.3)
117 | mimemagic (~> 0.3.2)
118 | method_source (1.0.0)
119 | mimemagic (0.3.5)
120 | mini_mime (1.0.2)
121 | mini_portile2 (2.4.0)
122 | minitest (5.14.1)
123 | msgpack (1.3.3)
124 | mustermann (1.1.1)
125 | ruby2_keywords (~> 0.0.1)
126 | nio4r (2.5.2)
127 | nokogiri (1.10.9)
128 | mini_portile2 (~> 2.4.0)
129 | opengraph_parser (0.2.3)
130 | addressable
131 | nokogiri
132 | pg (1.2.3)
133 | pry (0.13.1)
134 | coderay (~> 1.1)
135 | method_source (~> 1.0)
136 | pry-rails (0.3.9)
137 | pry (>= 0.10.4)
138 | public_suffix (4.0.5)
139 | puma (3.12.6)
140 | rack (2.2.3)
141 | rack-protection (2.0.8.1)
142 | rack
143 | rack-proxy (0.6.5)
144 | rack
145 | rack-test (1.1.0)
146 | rack (>= 1.0, < 3)
147 | rails (6.0.3.2)
148 | actioncable (= 6.0.3.2)
149 | actionmailbox (= 6.0.3.2)
150 | actionmailer (= 6.0.3.2)
151 | actionpack (= 6.0.3.2)
152 | actiontext (= 6.0.3.2)
153 | actionview (= 6.0.3.2)
154 | activejob (= 6.0.3.2)
155 | activemodel (= 6.0.3.2)
156 | activerecord (= 6.0.3.2)
157 | activestorage (= 6.0.3.2)
158 | activesupport (= 6.0.3.2)
159 | bundler (>= 1.3.0)
160 | railties (= 6.0.3.2)
161 | sprockets-rails (>= 2.0.0)
162 | rails-dom-testing (2.0.3)
163 | activesupport (>= 4.2.0)
164 | nokogiri (>= 1.6)
165 | rails-html-sanitizer (1.3.0)
166 | loofah (~> 2.3)
167 | railties (6.0.3.2)
168 | actionpack (= 6.0.3.2)
169 | activesupport (= 6.0.3.2)
170 | method_source
171 | rake (>= 0.8.7)
172 | thor (>= 0.20.3, < 2.0)
173 | rainbow (3.0.0)
174 | rake (13.0.1)
175 | rb-fsevent (0.10.4)
176 | rb-inotify (0.10.1)
177 | ffi (~> 1.0)
178 | redis (4.2.1)
179 | regexp_parser (1.7.1)
180 | rspec (3.9.0)
181 | rspec-core (~> 3.9.0)
182 | rspec-expectations (~> 3.9.0)
183 | rspec-mocks (~> 3.9.0)
184 | rspec-core (3.9.2)
185 | rspec-support (~> 3.9.3)
186 | rspec-expectations (3.9.2)
187 | diff-lcs (>= 1.2.0, < 2.0)
188 | rspec-support (~> 3.9.0)
189 | rspec-mocks (3.9.1)
190 | diff-lcs (>= 1.2.0, < 2.0)
191 | rspec-support (~> 3.9.0)
192 | rspec-support (3.9.3)
193 | ruby2_keywords (0.0.2)
194 | ruby_dep (1.5.0)
195 | rubyzip (2.3.0)
196 | sass-rails (6.0.0)
197 | sassc-rails (~> 2.1, >= 2.1.1)
198 | sassc (2.4.0)
199 | ffi (~> 1.9)
200 | sassc-rails (2.1.2)
201 | railties (>= 4.0.0)
202 | sassc (>= 2.0)
203 | sprockets (> 3.0)
204 | sprockets-rails
205 | tilt
206 | selenium-webdriver (3.142.7)
207 | childprocess (>= 0.5, < 4.0)
208 | rubyzip (>= 1.2.2)
209 | sidekiq (6.0.7)
210 | connection_pool (>= 2.2.2)
211 | rack (~> 2.0)
212 | rack-protection (>= 2.0.0)
213 | redis (>= 4.1.0)
214 | sidekiq-failures (1.0.0)
215 | sidekiq (>= 4.0.0)
216 | sidekiq_alive (2.0.2)
217 | sidekiq
218 | sinatra
219 | sinatra (2.0.8.1)
220 | mustermann (~> 1.0)
221 | rack (~> 2.0)
222 | rack-protection (= 2.0.8.1)
223 | tilt (~> 2.0)
224 | sprockets (4.0.2)
225 | concurrent-ruby (~> 1.0)
226 | rack (> 1, < 3)
227 | sprockets-rails (3.2.1)
228 | actionpack (>= 4.0)
229 | activesupport (>= 4.0)
230 | sprockets (>= 3.0.0)
231 | thor (1.0.1)
232 | thread_safe (0.3.6)
233 | tilt (2.0.10)
234 | turbolinks (5.2.1)
235 | turbolinks-source (~> 5.2)
236 | turbolinks-source (5.2.0)
237 | twitter-text (3.1.0)
238 | idn-ruby
239 | unf (~> 0.1.0)
240 | tzinfo (1.2.7)
241 | thread_safe (~> 0.1)
242 | unf (0.1.4)
243 | unf_ext
244 | unf_ext (0.0.7.7)
245 | web-console (4.0.3)
246 | actionview (>= 6.0.0)
247 | activemodel (>= 6.0.0)
248 | bindex (>= 0.4.0)
249 | railties (>= 6.0.0)
250 | webdrivers (4.4.1)
251 | nokogiri (~> 1.6)
252 | rubyzip (>= 1.3.0)
253 | selenium-webdriver (>= 3.0, < 4.0)
254 | webpacker (4.2.2)
255 | activesupport (>= 4.2)
256 | rack-proxy (>= 0.6.1)
257 | railties (>= 4.2)
258 | websocket-driver (0.7.2)
259 | websocket-extensions (>= 0.1.0)
260 | websocket-extensions (0.1.5)
261 | xpath (3.2.0)
262 | nokogiri (~> 1.8)
263 | zeitwerk (2.3.0)
264 |
265 | PLATFORMS
266 | ruby
267 |
268 | DEPENDENCIES
269 | bootsnap (>= 1.4.2)
270 | byebug
271 | capybara (>= 2.15)
272 | http
273 | httplog
274 | jbuilder (~> 2.7)
275 | listen (>= 3.0.5, < 3.2)
276 | opengraph_parser
277 | pg (>= 0.18, < 2.0)
278 | pry-rails
279 | puma (~> 3.12)
280 | rails (~> 6.0.3.1)
281 | redis (~> 4.0)
282 | rspec (~> 3.9)
283 | sass-rails (~> 6)
284 | selenium-webdriver
285 | sidekiq (~> 6.0)
286 | sidekiq-failures
287 | sidekiq_alive (~> 2.0)
288 | turbolinks (~> 5)
289 | twitter-text
290 | tzinfo-data
291 | web-console (>= 3.3.0)
292 | webdrivers
293 | webpacker (~> 4.0)
294 |
295 | RUBY VERSION
296 | ruby 2.6.6p146
297 |
298 | BUNDLED WITH
299 | 2.1.4
300 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | project_name := rails-k8s-demo
2 | image_name := quay.io/lewagon/$(project_name)
3 | latest_sha := $(shell git rev-parse --verify --short HEAD)
4 | rails_master_key := ""
5 | db_connection_string := ""
6 |
7 | ci-deploy:
8 | echo "Upgrading/installing release to specified Digital Ocean cluster"
9 | helm upgrade $(project_name) helm --install \
10 | --atomic --cleanup-on-fail \
11 | --set-string image.tag=$(latest_sha) \
12 | --set-string dbConnectionString=$(db_connection_string)
13 |
14 | build-sha-cached:
15 | set -e; \
16 | DOCKER_BUILDKIT=1 \
17 | docker build \
18 | -f Dockerfile.prod \
19 | --build-arg RUBY_VERSION="2.6.5" \
20 | --build-arg PG_MAJOR="11" \
21 | --build-arg NODE_MAJOR="11" \
22 | --build-arg YARN_VERSION="1.22.4" \
23 | --build-arg BUNDLER_VERSION="2.1.0" \
24 | --cache-from $(image_name):latest \
25 | . \
26 | -t $(image_name):$(latest_sha);
27 |
28 | build-latest:
29 | set -e; \
30 | DOCKER_BUILDKIT=1 \
31 | docker build \
32 | -f Dockerfile.prod \
33 | --build-arg RUBY_VERSION="2.6.5" \
34 | --build-arg PG_MAJOR="11" \
35 | --build-arg NODE_MAJOR="11" \
36 | --build-arg YARN_VERSION="1.22.4" \
37 | --build-arg BUNDLER_VERSION="2.1.0" \
38 | . \
39 | -t $(image_name):latest;
40 |
41 | push-latest:
42 | docker push $(image_name):latest
43 |
44 | push-sha:
45 | docker push $(image_name):$(latest_sha)
46 |
47 | build-push-latest: build-latest push-latest
48 |
49 | build-ci: build-sha-cached push-sha
50 |
51 | # DO_POSTGRES_URL needs to be set to the connection string in the shell
52 | upgrade-dev:
53 | helm upgrade $(project_name) helm --install \
54 | --atomic --cleanup-on-fail --timeout=3m0s \
55 | --set-string dbConnectionString=$(DO_POSTGRES_URL)
56 |
57 | build-push-latest-upgrade-dev: build-push-latest upgrade-dev
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Le Wagon x Digital Ocean Rails-on-Kubernetes demo
2 |
3 | :construction: This setup is a work in progress :construction:
4 |
5 | An attempt at creating a standardized Docker/Kubernetes-Helm/GitHub Actions setup for use with Rails projects.
6 | A version of this setup (that is little bit further ahead) is now used in production for services available to [Le Wagon](https://www.lewagon.com) teachers, students, and alumni.
7 |
8 | ## Demo application
9 |
10 | A standard Action Cable chat application (Rails 6 + Stimulus) that uses [twitter-text](https://github.com/twitter/twitter-text/tree/master/rb) gem to extract free-formed URLs out of text and generate Open Graph previews for links. The dependency was chosen consciously, as it requires an external system library to function (`libidn11`), which makes it a perfect case for Docker.
11 |
12 | It requires Sidekiq, Redis, and PostgreSQL to run, which represents the standard deployment stack of production Rails apps.
13 |
14 | 
15 |
16 | ## Goals
17 |
18 | - Create a sensible approach to running fairly complex Rails projects in development with Docker
19 | - Generate reproducible and customizable configuration for production
20 | - Store everything related to deployments in the same repo as application code. Rely on GitHub one-way encrtypred secrets to store sensitive data like database connection strings.
21 | - Cater to a _"classic Rails stack"_: Rails, Puma, Sidekiq, Redis, PostgreSQL, Webpacker, Action Cable
22 | - Allow for only a certain amount of complexity: enough to fit inside a head of a single full-stack developer who may end up maintaining the projects.
23 | - Hide ugly details of Kubernetes management by relying on **1 environment/1 cluster** approach and letting Digital Ocean manage the master node.
24 | - Deploy with `git push origin master` or a PR merge on GitHub
25 | - Use any external Postgres database
26 | - Provide the easy migration from "Heroku way of doing things", once the application is past the MVP and needs to scale for a reasonable cost.
27 |
28 | ## Current status
29 |
30 | By constantly migrating legacy applications to this setup and starting new ones with the same setup in mind we make sure that all common production bugs will be squashed before we can _fully automate_ the generation of charts and configuration.
31 |
32 | ## Running locally
33 |
34 | We use [dip](https://github.com/bibendi/dip) by Misha Merkushin at [Evil Martians](https://evilmartians.com) to achieve a "classic CLI" feel while working with Docker under the hood.
35 |
36 | - `gem install dip`
37 | - `dip provision` to build all necessary images and setup the local DB
38 | - `dip up -d` to run all services in the background: `app`, `rails`, `sidekiq`, `webpacker` (runs `webpack-dev-server` for JS live reload), `postgres`, `redis`. Go to `localhost:3000` :tada:
39 | - `dip rails c` to visit console.
40 | - `dip down` to stop the music and go home.
41 |
42 | If you need to change project dependencies—run `dip bundle` and/or `dip yarn` after any changes to `Gemfile` or `package.json`.
43 |
44 | Check out `dip.yml` for other common shortcuts.
45 |
46 | Alternatively, you can launch `dip rails s`, `dip sidekiq`, and `dip webpacker` in respective tabs. This will allow for easier debugging with `byebug` (no need to attach to running Docker processes).
47 |
48 | 
49 |
50 | ## Running in production
51 |
52 | - Provision a cluster from [Digital Ocean Managed Kubernetes](https://www.digitalocean.com/products/kubernetes/), install Nginx Ingress Controller, create DNS A record, set up [jetstack/certmanager](https://cert-manager.io/docs/installation/kubernetes/#installing-with-helm) for SSL. _Expect futher explanation of those steps once the setup is out of internal testing._
53 | - Install Helm 3 on your machine. Run `make upgrade-dev` from project repo to deploy the release into the cluster.
54 | - Once you verified that the deployment from local machine works — feel free to fork the repo and set up the following secrets:
55 |
56 | 
57 |
58 | :warning: You don't have to use Quay as Docker image backend, in that case you can alter `Makefile` push commands to your own image hosting provider.
59 |
60 | - After you're done—a push to master will trigger a GitHub Action for deployment. See `deploy.yaml` for details.
61 |
62 | **:pray: Suggestions and PRs are welcome!**
63 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require_relative "config/application"
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../stylesheets .css
3 |
--------------------------------------------------------------------------------
/app/assets/images/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/app/assets/images/.keep
--------------------------------------------------------------------------------
/app/assets/images/do-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/app/assets/images/do-logo.png
--------------------------------------------------------------------------------
/app/assets/images/wagon-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/app/assets/images/wagon-logo.png
--------------------------------------------------------------------------------
/app/assets/stylesheets/application.css:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
6 | * vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | *
13 | *= require_tree .
14 | *= require_self
15 | */
16 |
--------------------------------------------------------------------------------
/app/channels/application_cable/channel.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Channel < ActionCable::Channel::Base
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/app/channels/application_cable/connection.rb:
--------------------------------------------------------------------------------
1 | module ApplicationCable
2 | class Connection < ActionCable::Connection::Base
3 | identified_by :username
4 |
5 | def connect
6 | self.username = find_username
7 | end
8 |
9 | private
10 |
11 | def find_username
12 | if (username = cookies.encrypted[:username])
13 | username
14 | else
15 | reject_unauthorized_connection
16 | end
17 | end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/channels/messages_channel.rb:
--------------------------------------------------------------------------------
1 | class MessagesChannel < ApplicationCable::Channel
2 | def subscribed
3 | stream_from "messages"
4 | end
5 |
6 | def unsubscribed
7 | # Any cleanup needed when channel is unsubscribed
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | before_action :authenticate
3 |
4 | private
5 |
6 | def authenticate
7 | if (username = cookies.encrypted[:username].presence)
8 | Current.username = username
9 | else
10 | redirect_to new_login_path
11 | end
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/app/controllers/logins_controller.rb:
--------------------------------------------------------------------------------
1 | class LoginsController < ApplicationController
2 | skip_before_action :authenticate
3 |
4 | def new
5 | redirect_to messages_path if cookies.encrypted[:username].present?
6 | end
7 |
8 | def create
9 | if params[:username].present?
10 | cookies.encrypted[:username] = params[:username]
11 | redirect_to messages_path
12 | else
13 | redirect_to new_login_path, alert: "Username can't be blank" unless username
14 | end
15 | end
16 |
17 | def destroy
18 | cookies.encrypted[:username] = nil
19 | redirect_to new_login_path
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/app/controllers/messages_controller.rb:
--------------------------------------------------------------------------------
1 | class MessagesController < ApplicationController
2 | def index
3 | @messages = Message.includes(:previews).last(20)
4 | end
5 |
6 | def create
7 | @message = Message.new(message_params)
8 | @message.author = Current.username
9 | if @message.save
10 | DeliverMessageJob.perform_later(@message.id)
11 | end
12 | end
13 |
14 | private
15 |
16 | def message_params
17 | params.require(:message).permit(:content, :delay)
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/login_helper.rb:
--------------------------------------------------------------------------------
1 | module LoginHelper
2 | end
3 |
--------------------------------------------------------------------------------
/app/helpers/messages_helper.rb:
--------------------------------------------------------------------------------
1 | module MessagesHelper
2 | def flex_position(message, &block)
3 | if message.author == Current.username
4 | content_tag(:div, capture(&block), class: "d-flex flex-row-reverse")
5 | else
6 | content_tag(:div, capture(&block), class: "d-flex flex-row")
7 | end
8 | end
9 | end
10 |
--------------------------------------------------------------------------------
/app/javascript/channels/consumer.js:
--------------------------------------------------------------------------------
1 | // Action Cable provides the framework to deal with WebSockets in Rails.
2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command.
3 |
4 | import { createConsumer } from "@rails/actioncable"
5 |
6 | export default createConsumer()
7 |
--------------------------------------------------------------------------------
/app/javascript/channels/index.js:
--------------------------------------------------------------------------------
1 | // Load all the channels within this directory and all subdirectories.
2 | // Channel files must be named *_channel.js.
3 |
4 | const channels = require.context('.', true, /_channel\.js$/)
5 | channels.keys().forEach(channels)
6 |
--------------------------------------------------------------------------------
/app/javascript/controllers/chat_controller.js:
--------------------------------------------------------------------------------
1 | import { Controller } from "stimulus";
2 | import consumer from "channels/consumer";
3 |
4 | function subscribeConsumerWithStimulusContext(ctx) {
5 | consumer.subscriptions.create("MessagesChannel", {
6 | connected() {
7 | console.log("Hello from AC!");
8 | },
9 |
10 | disconnected() {
11 | console.log("Goodbye from AC");
12 | },
13 |
14 | received(message) {
15 | switch (message.type) {
16 | case "message":
17 | console.log(message);
18 | ctx.insertMessage(message);
19 | ctx.clearForm(message.author);
20 | break;
21 | case "preview":
22 | console.log(message);
23 | ctx.insertPreview(message);
24 | break;
25 | }
26 | }
27 | });
28 | }
29 |
30 | export default class extends Controller {
31 | static targets = ["messages", "form"];
32 |
33 | connect() {
34 | console.log("Stimulus chat controller up");
35 | subscribeConsumerWithStimulusContext(this);
36 | this.messagesTarget.lastElementChild.scrollIntoView();
37 | }
38 |
39 | insertMessage(message) {
40 | this.messagesTarget.insertAdjacentHTML("beforeend", message.html);
41 | const username = this.data.get("username");
42 | const lastMessageRow = document.getElementById(`message-${message.id}`)
43 | .parentElement;
44 | if (message.author == username) {
45 | lastMessageRow.classList.replace("flex-row", "flex-row-reverse");
46 | }
47 | lastMessageRow.scrollIntoView();
48 | }
49 |
50 | insertPreview(preview) {
51 | const message = document.getElementById(`message-${preview.message_id}`);
52 | if (message) {
53 | const previews = message.querySelector(".previews");
54 | previews.insertAdjacentHTML("afterbegin", preview.html);
55 | message.scrollIntoView();
56 | }
57 | }
58 |
59 | clearForm(author) {
60 | if (author === this.data.get("username")) {
61 | this.formTarget.reset();
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/javascript/controllers/index.js:
--------------------------------------------------------------------------------
1 | // Load all the controllers within this directory and all subdirectories.
2 | // Controller files must be named *_controller.js.
3 |
4 | import { Application } from "stimulus"
5 | import { definitionsFromContext } from "stimulus/webpack-helpers"
6 |
7 | const application = Application.start()
8 | const context = require.context("controllers", true, /_controller\.js$/)
9 | application.load(definitionsFromContext(context))
10 |
--------------------------------------------------------------------------------
/app/javascript/packs/application.js:
--------------------------------------------------------------------------------
1 | require("@rails/ujs").start();
2 | require("turbolinks").start();
3 |
4 | import "controllers";
5 | import "../stylesheets/application";
6 |
--------------------------------------------------------------------------------
/app/javascript/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | @import "bootstrap";
2 | @import "chat";
3 |
--------------------------------------------------------------------------------
/app/javascript/stylesheets/chat.scss:
--------------------------------------------------------------------------------
1 | #chat {
2 | height: 100vh;
3 | display: flex;
4 | flex-direction: column;
5 |
6 | #navbar-container {
7 | flex: auto;
8 | }
9 |
10 | #messages-container {
11 | height: 80vh;
12 | flex: auto;
13 | overflow-y: auto;
14 | -webkit-overflow-scrolling: touch;
15 | }
16 |
17 | #chat-controls-container {
18 | flex: auto;
19 | }
20 | }
21 |
22 | .message-box {
23 | width: fit-content;
24 | }
25 |
26 | .preview-box {
27 | max-width: 500px;
28 | }
29 |
30 | // Disable these elements on xs screens:
31 |
32 | #chat-submit {
33 | display: none;
34 | }
35 |
36 | #chat-title {
37 | display: none;
38 | }
39 |
40 | // ===================================
41 |
42 | @media (min-width: 576px) {
43 | #chat-submit {
44 | display: inline;
45 | }
46 |
47 | #chat-title {
48 | display: inline;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/jobs/application_job.rb:
--------------------------------------------------------------------------------
1 | class ApplicationJob < ActiveJob::Base
2 | # Automatically retry jobs that encountered a deadlock
3 | # retry_on ActiveRecord::Deadlocked
4 |
5 | # Most jobs are safe to ignore if the underlying records are no longer available
6 | # discard_on ActiveJob::DeserializationError
7 | end
8 |
--------------------------------------------------------------------------------
/app/jobs/deliver_message_job.rb:
--------------------------------------------------------------------------------
1 | class DeliverMessageJob < ApplicationJob
2 | queue_as :default
3 | sidekiq_options retry: 0 # never retry
4 |
5 | def perform(message_id)
6 | @message = Message.find(message_id)
7 | MessageBroadcaster.call(@message)
8 | count_created = PreviewCreator.call(@message)
9 | PreviewBroadcaster.call(@message) if count_created.positive?
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/app/mailers/application_mailer.rb:
--------------------------------------------------------------------------------
1 | class ApplicationMailer < ActionMailer::Base
2 | default from: "from@example.com"
3 | layout "mailer"
4 | end
5 |
--------------------------------------------------------------------------------
/app/models/application_record.rb:
--------------------------------------------------------------------------------
1 | class ApplicationRecord < ActiveRecord::Base
2 | self.abstract_class = true
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/app/models/concerns/.keep
--------------------------------------------------------------------------------
/app/models/current.rb:
--------------------------------------------------------------------------------
1 | class Current < ActiveSupport::CurrentAttributes
2 | attribute :username
3 | end
4 |
--------------------------------------------------------------------------------
/app/models/message.rb:
--------------------------------------------------------------------------------
1 | class Message < ApplicationRecord
2 | has_many :previews, dependent: :destroy
3 | # gives extract_urls method to message
4 | include Twitter::TwitterText::Extractor
5 | validates :content, presence: true
6 |
7 | def urls
8 | extract_urls(content).map do |url|
9 | scheme_present?(url) ? url : "http://#{url}"
10 | end
11 | end
12 |
13 | private
14 |
15 | def scheme_present?(url)
16 | URI.parse(url).scheme.present?
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/app/models/preview.rb:
--------------------------------------------------------------------------------
1 | class Preview < ApplicationRecord
2 | belongs_to :message
3 | end
4 |
--------------------------------------------------------------------------------
/app/services/message_broadcaster.rb:
--------------------------------------------------------------------------------
1 | class MessageBroadcaster
2 | def self.call(message)
3 | new(message).broadcast_message
4 | end
5 |
6 | def initialize(message)
7 | @message = message
8 | end
9 |
10 | def broadcast_message
11 | ActionCable.server.broadcast(
12 | "messages",
13 | type: "message",
14 | id: @message.id,
15 | author: @message.author,
16 | html: ApplicationController.renderer.render(
17 | partial: "messages/message",
18 | locals: {message: @message}
19 | )
20 | )
21 | end
22 | end
23 |
--------------------------------------------------------------------------------
/app/services/preview_broadcaster.rb:
--------------------------------------------------------------------------------
1 | class PreviewBroadcaster
2 | def self.call(message)
3 | new(message).broadcast_previews
4 | end
5 |
6 | def initialize(message)
7 | @message = message
8 | end
9 |
10 | def broadcast_previews
11 | @message.previews.each do |preview|
12 | broadcast_preview(preview)
13 | end
14 | end
15 |
16 | private
17 |
18 | def broadcast_preview(preview)
19 | ActionCable.server.broadcast(
20 | "messages",
21 | type: "preview",
22 | id: preview.id,
23 | message_id: preview.message.id,
24 | html: ApplicationController.renderer.render(
25 | partial: "previews/preview",
26 | locals: {preview: preview}
27 | )
28 | )
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/app/services/preview_creator.rb:
--------------------------------------------------------------------------------
1 | class PreviewCreator
2 | def self.call(message)
3 | new(message).create_previews
4 | end
5 |
6 | def initialize(message)
7 | @message = message
8 | end
9 |
10 | def create_previews
11 | return 0 unless @message.urls.any?
12 |
13 | attrs = @message.urls.map { |url|
14 | # This makes an external HTTP call
15 | # TODO: offload to another job?
16 | ogp = OpenGraph.new(url)
17 | {
18 | title: ogp.title,
19 | description: ogp.description,
20 | image_url: ogp.images.first,
21 | url: url,
22 | }
23 | }
24 | @message.previews.create(attrs)
25 | @message.previews.count
26 | end
27 | end
28 |
--------------------------------------------------------------------------------
/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Le Wagon/Digital Ocean Rails DOKS demo
5 | <%= csrf_meta_tags %>
6 | <%= csp_meta_tag %>
7 |
8 |
9 |
10 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload', defer: true %>
11 | <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload', defer: true %>
12 | <%= stylesheet_pack_tag 'application', 'data-turbolinks-track': 'reload', defer: true %>
13 |
14 |
15 |
16 | <% if flash[:alert] %>
17 | <%= flash[:alert] %>
18 | <% end %>
19 |
20 | <%= yield %>
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/views/layouts/mailer.text.erb:
--------------------------------------------------------------------------------
1 | <%= yield %>
2 |
--------------------------------------------------------------------------------
/app/views/logins/new.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_with url: login_path do |f| %>
2 |
10 | <% end %>
11 |
--------------------------------------------------------------------------------
/app/views/messages/_message.html.erb:
--------------------------------------------------------------------------------
1 | <%= flex_position(message) do %>
2 |
5 |
6 | <%= message.content %>
7 |
8 |
9 | <%= message.author %>
10 | <%= l(message.created_at, format: :short) %>
11 |
12 |
13 |
14 |
15 | <%= render message.previews %>
16 |
17 |
18 | <% end %>
19 |
--------------------------------------------------------------------------------
/app/views/messages/index.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%= link_to "https://www.lewagon.com" do %>
6 | <%= image_tag "wagon-logo.png", width: 50, class: "p-1" %>
7 | <% end %>
8 | <%= link_to "https://www.digitalocean.com" do %>
9 | <%= image_tag "do-logo.png", width: 50, class: "p-1" %>
10 | <% end %>
11 |
12 |
13 |
14 | Rails on
15 | <%= link_to "DOKS", "https://www.digitalocean.com/products/kubernetes/"%>
16 | Demo
17 |
18 |
19 |
20 | <%= Current.username %> |
21 | <%= link_to "Log Out", login_path, method: :delete %>
22 |
23 |
24 |
25 |
26 |
27 |
28 | <%= render @messages %>
29 |
30 |
31 |
32 |
33 |
34 | <%= form_with model: Message.new,
35 | id: "chat_form",
36 | html: {
37 | autocomplete: "off"
38 | },
39 | data: {
40 | target: "chat.form"
41 | } do |f|
42 | %>
43 |
54 | <% end %>
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/app/views/previews/_preview.html.erb:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function(api) {
2 | var validEnv = ['development', 'test', 'production']
3 | var currentEnv = api.env()
4 | var isDevelopmentEnv = api.env('development')
5 | var isProductionEnv = api.env('production')
6 | var isTestEnv = api.env('test')
7 |
8 | if (!validEnv.includes(currentEnv)) {
9 | throw new Error(
10 | 'Please specify a valid `NODE_ENV` or ' +
11 | '`BABEL_ENV` environment variables. Valid values are "development", ' +
12 | '"test", and "production". Instead, received: ' +
13 | JSON.stringify(currentEnv) +
14 | '.'
15 | )
16 | }
17 |
18 | return {
19 | presets: [
20 | isTestEnv && [
21 | require('@babel/preset-env').default,
22 | {
23 | targets: {
24 | node: 'current'
25 | }
26 | }
27 | ],
28 | (isProductionEnv || isDevelopmentEnv) && [
29 | require('@babel/preset-env').default,
30 | {
31 | forceAllTransforms: true,
32 | useBuiltIns: 'entry',
33 | corejs: 3,
34 | modules: false,
35 | exclude: ['transform-typeof-symbol']
36 | }
37 | ]
38 | ].filter(Boolean),
39 | plugins: [
40 | require('babel-plugin-macros'),
41 | require('@babel/plugin-syntax-dynamic-import').default,
42 | isTestEnv && require('babel-plugin-dynamic-import-node'),
43 | require('@babel/plugin-transform-destructuring').default,
44 | [
45 | require('@babel/plugin-proposal-class-properties').default,
46 | {
47 | loose: true
48 | }
49 | ],
50 | [
51 | require('@babel/plugin-proposal-object-rest-spread').default,
52 | {
53 | useBuiltIns: true
54 | }
55 | ],
56 | [
57 | require('@babel/plugin-transform-runtime').default,
58 | {
59 | helpers: false,
60 | regenerator: true,
61 | corejs: false
62 | }
63 | ],
64 | [
65 | require('@babel/plugin-transform-regenerator').default,
66 | {
67 | async: false
68 | }
69 | ]
70 | ].filter(Boolean)
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | # frozen_string_literal: true
3 |
4 | #
5 | # This file was generated by Bundler.
6 | #
7 | # The application 'bundle' is installed as part of a gem, and
8 | # this file is here to facilitate running it.
9 | #
10 |
11 | require "rubygems"
12 |
13 | m = Module.new do
14 | module_function
15 |
16 | def invoked_as_script?
17 | File.expand_path($0) == File.expand_path(__FILE__)
18 | end
19 |
20 | def env_var_version
21 | ENV["BUNDLER_VERSION"]
22 | end
23 |
24 | def cli_arg_version
25 | return unless invoked_as_script? # don't want to hijack other binstubs
26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27 | bundler_version = nil
28 | update_index = nil
29 | ARGV.each_with_index do |a, i|
30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
31 | bundler_version = a
32 | end
33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
34 | bundler_version = $1 || ">= 0.a"
35 | update_index = i
36 | end
37 | bundler_version
38 | end
39 |
40 | def gemfile
41 | gemfile = ENV["BUNDLE_GEMFILE"]
42 | return gemfile if gemfile && !gemfile.empty?
43 |
44 | File.expand_path("../../Gemfile", __FILE__)
45 | end
46 |
47 | def lockfile
48 | lockfile =
49 | case File.basename(gemfile)
50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
51 | else "#{gemfile}.lock"
52 | end
53 | File.expand_path(lockfile)
54 | end
55 |
56 | def lockfile_version
57 | return unless File.file?(lockfile)
58 | lockfile_contents = File.read(lockfile)
59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
60 | Regexp.last_match(1)
61 | end
62 |
63 | def bundler_version
64 | @bundler_version ||= begin
65 | env_var_version || cli_arg_version ||
66 | lockfile_version || "#{Gem::Requirement.default}.a"
67 | end
68 | end
69 |
70 | def load_bundler!
71 | ENV["BUNDLE_GEMFILE"] ||= gemfile
72 |
73 | # must dup string for RG < 1.8 compatibility
74 | activate_bundler(bundler_version.dup)
75 | end
76 |
77 | def activate_bundler(bundler_version)
78 | if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0")
79 | bundler_version = "< 2"
80 | end
81 | gem_error = activation_error_handling do
82 | gem "bundler", bundler_version
83 | end
84 | return if gem_error.nil?
85 | require_error = activation_error_handling do
86 | require "bundler/version"
87 | end
88 | return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION))
89 | warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`"
90 | exit 42
91 | end
92 |
93 | def activation_error_handling
94 | yield
95 | nil
96 | rescue StandardError, LoadError => e
97 | e
98 | end
99 | end
100 |
101 | m.load_bundler!
102 |
103 | if m.invoked_as_script?
104 | load Gem.bin_path("bundler", "bundle")
105 | end
106 |
--------------------------------------------------------------------------------
/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | APP_PATH = File.expand_path('../config/application', __dir__)
8 | require_relative '../config/boot'
9 | require 'rails/commands'
10 |
--------------------------------------------------------------------------------
/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | begin
3 | load File.expand_path('../spring', __FILE__)
4 | rescue LoadError => e
5 | raise unless e.message.include?('spring')
6 | end
7 | require_relative '../config/boot'
8 | require 'rake'
9 | Rake.application.run
10 |
--------------------------------------------------------------------------------
/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path('..', __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to setup or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies
21 | # system('bin/yarn')
22 |
23 | # puts "\n== Copying sample files =="
24 | # unless File.exist?('config/database.yml')
25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
26 | # end
27 |
28 | puts "\n== Preparing database =="
29 | system! 'bin/rails db:prepare'
30 |
31 | puts "\n== Removing old logs and tempfiles =="
32 | system! 'bin/rails log:clear tmp:clear'
33 |
34 | puts "\n== Restarting application server =="
35 | system! 'bin/rails restart'
36 | end
37 |
--------------------------------------------------------------------------------
/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads Spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
12 | if spring
13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path
14 | gem 'spring', spring.version
15 | require 'spring/binstub'
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/bin/webpack:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "rubygems"
11 | require "bundler/setup"
12 |
13 | require "webpacker"
14 | require "webpacker/webpack_runner"
15 |
16 | APP_ROOT = File.expand_path("..", __dir__)
17 | Dir.chdir(APP_ROOT) do
18 | Webpacker::WebpackRunner.run(ARGV)
19 | end
20 |
--------------------------------------------------------------------------------
/bin/webpack-dev-server:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
4 | ENV["NODE_ENV"] ||= "development"
5 |
6 | require "pathname"
7 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
8 | Pathname.new(__FILE__).realpath)
9 |
10 | require "rubygems"
11 | require "bundler/setup"
12 |
13 | require "webpacker"
14 | require "webpacker/dev_server_runner"
15 |
16 | APP_ROOT = File.expand_path("..", __dir__)
17 | Dir.chdir(APP_ROOT) do
18 | Webpacker::DevServerRunner.run(ARGV)
19 | end
20 |
--------------------------------------------------------------------------------
/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | begin
5 | exec "yarnpkg", *ARGV
6 | rescue Errno::ENOENT
7 | $stderr.puts "Yarn executable was not detected in the system."
8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
9 | exit 1
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative "config/environment"
4 |
5 | run Rails.application
6 |
--------------------------------------------------------------------------------
/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative "boot"
2 |
3 | require "rails"
4 | # Pick the frameworks you want:
5 | require "active_model/railtie"
6 | require "active_job/railtie"
7 | require "active_record/railtie"
8 | # require "active_storage/engine"
9 | require "action_controller/railtie"
10 | require "action_mailer/railtie"
11 | # require "action_mailbox/engine"
12 | # require "action_text/engine"
13 | require "action_view/railtie"
14 | require "action_cable/engine"
15 | require "sprockets/railtie"
16 | require "rails/test_unit/railtie"
17 |
18 | # Require the gems listed in Gemfile, including any gems
19 | # you've limited to :test, :development, or :production.
20 | Bundler.require(*Rails.groups)
21 |
22 | module RailsK8sDemo
23 | class Application < Rails::Application
24 | # Initialize configuration defaults for originally generated Rails version.
25 | config.load_defaults 6.0
26 |
27 | # Settings in config/environments/* take precedence over those specified here.
28 | # Application configuration can go into files in config/initializers
29 | # -- all .rb files in that directory are automatically loaded after loading
30 | # the framework and any gems in your application.
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2 |
3 | require "bundler/setup" # Set up gems listed in the Gemfile.
4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations.
5 |
--------------------------------------------------------------------------------
/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: redis
3 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
4 |
5 | test:
6 | adapter: test
7 |
8 | production:
9 | adapter: redis
10 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
11 | channel_prefix: review_scraper_production
12 |
--------------------------------------------------------------------------------
/config/credentials.yml.enc:
--------------------------------------------------------------------------------
1 | zbCgZmsmpEUg7w0Nve4qHU2lHvKDXkzCSLR98vGVUTapJAc+7abv6NxoFEHCnc5AEaJa3rJjPRuA0zEox2vQlge5Y6cdGTloXNhHDN7k4I+jDiXggxNYiV2UbpOPsKTz+n8gDxclJQOQUpz3UCrUKxlluAirva31FAkHkvSqjEuRkXdUH5vN3GVW/oRyC1zsbx4Q8pb5A+EKFzPAT/lAxZjkvOVo2EDiavTCHV/xND0mP1kqU6RrlGO7GVdlRxHZW6nKi7nwdi7lW5Yqqn+PZoL8P8/QsxJB2lL4VV3qjnnN93GBBvqN/46SORvAKB4llGcCjwQ9KV3UMSs9zSY2vpuaFMd9TOobxF5lCiqHGPAcHz0xt+9ZgvZX0yAegVkdEfw8r2lPixaBCEiizmtIHHWAlUfcxJx4j6jN--VVJAk/F+B7CvG485--BFKaGf6oTPwHYzHKcH1yLw==
--------------------------------------------------------------------------------
/config/database.yml:
--------------------------------------------------------------------------------
1 | default: &default
2 | adapter: postgresql
3 | encoding: unicode
4 | # For details on connection pooling, see Rails configuration guide
5 | # https://guides.rubyonrails.org/configuring.html#database-pooling
6 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
7 |
8 | development:
9 | <<: *default
10 | database: db_development
11 | url: <%= ENV['DATABASE_URL'] %>
12 |
13 | test:
14 | <<: *default
15 | database: db_test
16 | url: <%= ENV['DATABASE_URL'] %>
17 |
18 | production:
19 | <<: *default
20 | database: rails-k8s-demo-production
21 | url: <%= ENV['DATABASE_URL'] %>
22 |
--------------------------------------------------------------------------------
/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative "application"
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Docker will expose port on one of those subnets, so we permit them
5 | config.web_console.permissions = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
6 |
7 | # Sadly, not documented anywhere, but required since Rails 6
8 | config.hosts << ".ngrok.io"
9 |
10 | # In the development environment your application's code is reloaded on
11 | # every request. This slows down response time but is perfect for development
12 | # since you don't have to restart the web server when you make code changes.
13 | config.cache_classes = false
14 |
15 | # Do not eager load code on boot.
16 | config.eager_load = false
17 |
18 | # Show full error reports.
19 | config.consider_all_requests_local = true
20 |
21 | # Enable/disable caching. By default caching is disabled.
22 | # Run rails dev:cache to toggle caching.
23 | if Rails.root.join("tmp", "caching-dev.txt").exist?
24 | config.action_controller.perform_caching = true
25 | config.action_controller.enable_fragment_cache_logging = true
26 |
27 | config.cache_store = :memory_store
28 | config.public_file_server.headers = {
29 | "Cache-Control" => "public, max-age=#{2.days.to_i}",
30 | }
31 | else
32 | config.action_controller.perform_caching = false
33 |
34 | config.cache_store = :null_store
35 | end
36 |
37 | config.active_job.queue_adapter = :sidekiq
38 |
39 | # Don't care if the mailer can't send.
40 | config.action_mailer.raise_delivery_errors = false
41 |
42 | config.action_mailer.perform_caching = false
43 |
44 | # Print deprecation notices to the Rails logger.
45 | config.active_support.deprecation = :log
46 |
47 | # Raise an error on page load if there are pending migrations.
48 | config.active_record.migration_error = :page_load
49 |
50 | # Highlight code that triggered database queries in logs.
51 | config.active_record.verbose_query_logs = true
52 |
53 | # Debug mode disables concatenation and preprocessing of assets.
54 | # This option may cause significant delays in view rendering with a large
55 | # number of complex assets.
56 | config.assets.debug = true
57 |
58 | # Suppress logger output for asset requests.
59 | config.assets.quiet = true
60 |
61 | # Raises error for missing translations.
62 | # config.action_view.raise_on_missing_translations = true
63 |
64 | # Use an evented file watcher to asynchronously detect changes in source code,
65 | # routes, locales, etc. This feature depends on the listen gem.
66 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
67 | end
68 |
69 | HttpLog.configure do |config|
70 | # Enable or disable all logging
71 | config.enabled = true
72 |
73 | # You can assign a different logger or method to call on that logger
74 | config.logger = Logger.new($stdout)
75 | config.logger_method = :log
76 |
77 | # I really wouldn't change this...
78 | config.severity = Logger::Severity::DEBUG
79 |
80 | # Tweak which parts of the HTTP cycle to log...
81 | config.log_connect = true
82 | config.log_request = true
83 | config.log_headers = true
84 | config.log_data = true
85 | config.log_status = true
86 | config.log_response = false
87 | config.log_benchmark = true
88 |
89 | # ...or log all request as a single line by setting this to `true`
90 | config.compact_log = true
91 |
92 | # You can also log in JSON format
93 | config.json_log = false
94 |
95 | # Prettify the output - see below
96 | config.color = false
97 |
98 | # Limit logging based on URL patterns
99 | config.url_whitelist_pattern = nil
100 | config.url_blacklist_pattern = nil
101 | end
102 |
--------------------------------------------------------------------------------
/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | Rails.application.configure do
2 | # Settings specified here will take precedence over those in config/application.rb.
3 |
4 | # Code is not reloaded between requests.
5 | config.cache_classes = true
6 |
7 | # Eager load code on boot. This eager loads most of Rails and
8 | # your application in memory, allowing both threaded web servers
9 | # and those relying on copy on write to perform better.
10 | # Rake tasks automatically ignore this option for performance.
11 | config.eager_load = true
12 |
13 | # Full error reports are disabled and caching is turned on.
14 | config.consider_all_requests_local = false
15 | config.action_controller.perform_caching = true
16 |
17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
19 | config.require_master_key = true
20 |
21 | # Disable serving static files from the `/public` folder by default since
22 | # Apache or NGINX already handles this.
23 | config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present?
24 |
25 | # Compress CSS using a preprocessor.
26 | # config.assets.css_compressor = :sass
27 |
28 | # Do not fallback to assets pipeline if a precompiled asset is missed.
29 | config.assets.compile = false
30 |
31 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
32 | config.action_controller.asset_host = "dibjvtbcs979a.cloudfront.net"
33 |
34 | # Specifies the header that your server uses for sending files.
35 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
36 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
37 |
38 | # Mount Action Cable outside main process or domain.
39 | # config.action_cable.mount_path = nil
40 | # config.action_cable.url = 'wss://example.com/cable'
41 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
42 |
43 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
44 | config.force_ssl = true
45 |
46 |
47 | # Use the lowest log level to ensure availability of diagnostic information
48 | # when problems arise.
49 | config.log_level = :debug
50 |
51 | # Prepend all log lines with the following tags.
52 | config.log_tags = [:request_id]
53 |
54 | # Use a different cache store in production.
55 | # config.cache_store = :mem_cache_store
56 |
57 | # Use a real queuing backend for Active Job (and separate queues per environment).
58 | config.active_job.queue_adapter = :sidekiq
59 | # config.active_job.queue_name_prefix = "review_scraper_production"
60 |
61 | config.action_mailer.perform_caching = false
62 |
63 | # Ignore bad email addresses and do not raise email delivery errors.
64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
65 | # config.action_mailer.raise_delivery_errors = false
66 |
67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
68 | # the I18n.default_locale when a translation cannot be found).
69 | config.i18n.fallbacks = true
70 |
71 | # Send deprecation notices to registered listeners.
72 | config.active_support.deprecation = :notify
73 |
74 | # Use default logging formatter so that PID and timestamp are not suppressed.
75 | config.log_formatter = ::Logger::Formatter.new
76 |
77 | # Use a different logger for distributed setups.
78 | # require 'syslog/logger'
79 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
80 |
81 | if ENV["RAILS_LOG_TO_STDOUT"].present?
82 | logger = ActiveSupport::Logger.new(STDOUT)
83 | logger.formatter = config.log_formatter
84 | config.logger = ActiveSupport::TaggedLogging.new(logger)
85 | end
86 |
87 | # Do not dump schema after migrations.
88 | config.active_record.dump_schema_after_migration = false
89 |
90 | # Inserts middleware to perform automatic connection switching.
91 | # The `database_selector` hash is used to pass options to the DatabaseSelector
92 | # middleware. The `delay` is used to determine how long to wait after a write
93 | # to send a subsequent read to the primary.
94 | #
95 | # The `database_resolver` class is used by the middleware to determine which
96 | # database is appropriate to use based on the time delay.
97 | #
98 | # The `database_resolver_context` class is used by the middleware to set
99 | # timestamps for the last write to the primary. The resolver uses the context
100 | # class timestamps to determine how long to wait before reading from the
101 | # replica.
102 | #
103 | # By default Rails will store a last write timestamp in the session. The
104 | # DatabaseSelector middleware is designed as such you can define your own
105 | # strategy for connection switching and pass that into the middleware through
106 | # these configuration options.
107 | # config.active_record.database_selector = { delay: 2.seconds }
108 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
109 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
110 | end
111 |
--------------------------------------------------------------------------------
/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | # The test environment is used exclusively to run your application's
2 | # test suite. You never need to work with it otherwise. Remember that
3 | # your test database is "scratch space" for the test suite and is wiped
4 | # and recreated between test runs. Don't rely on the data there!
5 |
6 | Rails.application.configure do
7 | # Settings specified here will take precedence over those in config/application.rb.
8 |
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot. This avoids loading your whole application
12 | # just for the purpose of running a single test. If you are using a tool that
13 | # preloads Rails for running tests, you may have to set it to true.
14 | config.eager_load = false
15 |
16 | # Configure public file server for tests with Cache-Control for performance.
17 | config.public_file_server.enabled = true
18 | config.public_file_server.headers = {
19 | "Cache-Control" => "public, max-age=#{1.hour.to_i}",
20 | }
21 |
22 | # Show full error reports and disable caching.
23 | config.consider_all_requests_local = true
24 | config.action_controller.perform_caching = false
25 | config.cache_store = :null_store
26 |
27 | # Raise exceptions instead of rendering exception templates.
28 | config.action_dispatch.show_exceptions = false
29 |
30 | # Disable request forgery protection in test environment.
31 | config.action_controller.allow_forgery_protection = false
32 |
33 | config.action_mailer.perform_caching = false
34 |
35 | # Tell Action Mailer not to deliver emails to the real world.
36 | # The :test delivery method accumulates sent emails in the
37 | # ActionMailer::Base.deliveries array.
38 | config.action_mailer.delivery_method = :test
39 |
40 | # Print deprecation notices to the stderr.
41 | config.active_support.deprecation = :stderr
42 |
43 | # Raises error for missing translations.
44 | # config.action_view.raise_on_missing_translations = true
45 | end
46 |
--------------------------------------------------------------------------------
/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = "1.0"
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join("node_modules")
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7 | # Rails.backtrace_cleaner.remove_silencers!
8 |
--------------------------------------------------------------------------------
/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 | # # If you are using webpack-dev-server then specify webpack-dev-server host
15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
16 |
17 | # # Specify URI for violation reports
18 | # # policy.report_uri "/csp-violation-report-endpoint"
19 | # end
20 |
21 | # If you are using UJS then enable automatic nonce generation
22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
23 |
24 | # Set the nonce only to specific directives
25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
26 |
27 | # Report CSP violations to a specified URI
28 | # For further information see the following documentation:
29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
30 | # Rails.application.config.content_security_policy_report_only = true
31 |
--------------------------------------------------------------------------------
/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [:password]
5 |
--------------------------------------------------------------------------------
/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/config/initializers/sidekiq_redis.rb:
--------------------------------------------------------------------------------
1 | $redis = Redis.new(url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"), password: ENV["REDIS_PASSWORD"])
2 |
3 | Sidekiq.configure_server do |config|
4 | config.redis = {url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"), password: ENV["REDIS_PASSWORD"]}
5 | end
6 |
7 | Sidekiq.configure_client do |config|
8 | config.redis = {url: ENV.fetch("REDIS_URL", "redis://localhost:6379/0"), password: ENV["REDIS_PASSWORD"]}
9 | end
10 |
11 | Redis.exists_returns_integer = true
12 |
--------------------------------------------------------------------------------
/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
11 | # To enable root element in JSON for ActiveRecord objects.
12 | # ActiveSupport.on_load(:active_record) do
13 | # self.include_root_in_json = true
14 | # end
15 |
--------------------------------------------------------------------------------
/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers: a minimum and maximum.
3 | # Any libraries that use thread pools should be configured to match
4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum
5 | # and maximum; this matches the default thread size of Active Record.
6 | #
7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
9 | threads min_threads_count, max_threads_count
10 |
11 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000.
12 | #
13 | port ENV.fetch("PORT") { 3000 }
14 |
15 | # Specifies the `environment` that Puma will run in.
16 | #
17 | environment ENV.fetch("RAILS_ENV") { "development" }
18 |
19 | # Specifies the `pidfile` that Puma will use.
20 | # pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
21 |
22 | # Specifies the number of `workers` to boot in clustered mode.
23 | # Workers are forked web server processes. If using threads and workers together
24 | # the concurrency of the application would be max `threads` * `workers`.
25 | # Workers do not work on JRuby or Windows (both of which do not support
26 | # processes).
27 | #
28 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 }
29 |
30 | # Use the `preload_app!` method when specifying a `workers` number.
31 | # This directive tells Puma to first boot the application and load code
32 | # before forking the application. This takes advantage of Copy On Write
33 | # process behavior so workers use less memory.
34 | #
35 | # preload_app!
36 |
37 | # Allow puma to be restarted by `rails restart` command.
38 | plugin :tmp_restart
39 |
--------------------------------------------------------------------------------
/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | root to: "logins#new"
3 | resource :login, only: [:new, :create, :destroy]
4 |
5 | resources :messages, only: [:index, :create]
6 |
7 | # Sidekiq dashboard
8 | require "sidekiq/web"
9 | mount Sidekiq::Web, at: "/sidekiq"
10 | end
11 |
--------------------------------------------------------------------------------
/config/sidekiq.yml:
--------------------------------------------------------------------------------
1 | :concurrency: 8
2 | :timeout: 30
3 | :verbose: true
4 | :queues:
5 | - default
6 |
--------------------------------------------------------------------------------
/config/spring.rb:
--------------------------------------------------------------------------------
1 | Spring.watch(
2 | ".ruby-version",
3 | ".rbenv-vars",
4 | "tmp/restart.txt",
5 | "tmp/caching-dev.txt"
6 | )
7 |
--------------------------------------------------------------------------------
/config/webpack/development.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/environment.js:
--------------------------------------------------------------------------------
1 | const { environment } = require('@rails/webpacker')
2 |
3 | const webpack = require('webpack')
4 | environment.plugins.append(
5 | 'Provide',
6 | new webpack.ProvidePlugin({
7 | $: 'jquery',
8 | jQuery: 'jquery',
9 | Popper: ['popper.js', 'default']
10 | })
11 | )
12 |
13 | module.exports = environment
14 |
--------------------------------------------------------------------------------
/config/webpack/production.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'production'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpack/test.js:
--------------------------------------------------------------------------------
1 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'
2 |
3 | const environment = require('./environment')
4 |
5 | module.exports = environment.toWebpackConfig()
6 |
--------------------------------------------------------------------------------
/config/webpacker.yml:
--------------------------------------------------------------------------------
1 | # Note: You must restart bin/webpack-dev-server for changes to take effect
2 |
3 | default: &default
4 | source_path: app/javascript
5 | source_entry_path: packs
6 | public_root_path: public
7 | public_output_path: packs
8 | cache_path: tmp/cache/webpacker
9 | check_yarn_integrity: false
10 | webpack_compile_output: false
11 |
12 | # Additional paths webpack should lookup modules
13 | # ['app/assets', 'engine/foo/app/assets']
14 | resolved_paths: []
15 |
16 | # Reload manifest.json on all requests so we reload latest compiled packs
17 | cache_manifest: false
18 |
19 | # Extract and emit a css file
20 | extract_css: false
21 |
22 | static_assets_extensions:
23 | - .jpg
24 | - .jpeg
25 | - .png
26 | - .gif
27 | - .tiff
28 | - .ico
29 | - .svg
30 | - .eot
31 | - .otf
32 | - .ttf
33 | - .woff
34 | - .woff2
35 |
36 | extensions:
37 | - .mjs
38 | - .js
39 | - .sass
40 | - .scss
41 | - .css
42 | - .module.sass
43 | - .module.scss
44 | - .module.css
45 | - .png
46 | - .svg
47 | - .gif
48 | - .jpeg
49 | - .jpg
50 |
51 | development:
52 | <<: *default
53 | compile: true
54 |
55 | # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
56 | check_yarn_integrity: true
57 |
58 | # Reference: https://webpack.js.org/configuration/dev-server/
59 | dev_server:
60 | https: false
61 | host: localhost
62 | port: 3035
63 | public: localhost:3035
64 | hmr: false
65 | # Inline should be set to true if using HMR
66 | inline: true
67 | overlay: true
68 | compress: true
69 | disable_host_check: true
70 | use_local_ip: false
71 | quiet: false
72 | headers:
73 | "Access-Control-Allow-Origin": "*"
74 | watch_options:
75 | ignored: "**/node_modules/**"
76 | poll: true
77 |
78 | test:
79 | <<: *default
80 | compile: true
81 |
82 | # Compile test packs to a separate directory
83 | public_output_path: packs-test
84 |
85 | production:
86 | <<: *default
87 |
88 | # Production depends on precompilation of packs prior to booting for performance.
89 | compile: false
90 |
91 | # Extract and emit a css file
92 | extract_css: true
93 |
94 | # Cache manifest.json for performance
95 | cache_manifest: true
96 |
--------------------------------------------------------------------------------
/db/migrate/20191126104811_create_messages.rb:
--------------------------------------------------------------------------------
1 | class CreateMessages < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :messages do |t|
4 | t.string :content
5 | t.string :author
6 |
7 | t.timestamps
8 | end
9 | end
10 | end
11 |
--------------------------------------------------------------------------------
/db/migrate/20191203142803_create_previews.rb:
--------------------------------------------------------------------------------
1 | class CreatePreviews < ActiveRecord::Migration[6.0]
2 | def change
3 | create_table :previews do |t|
4 | t.string :title
5 | t.string :url
6 | t.string :image_url
7 | t.text :description
8 | t.references :message, null: false, foreign_key: true
9 | t.timestamps
10 | end
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/db/schema.rb:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from the current state of the database. Instead
2 | # of editing this file, please use the migrations feature of Active Record to
3 | # incrementally modify your database, and then regenerate this schema definition.
4 | #
5 | # This file is the source Rails uses to define your schema when running `rails
6 | # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7 | # be faster and is potentially less error prone than running all of your
8 | # migrations from scratch. Old migrations may fail to apply correctly if those
9 | # migrations use external dependencies or application code.
10 | #
11 | # It's strongly recommended that you check this file into your version control system.
12 |
13 | ActiveRecord::Schema.define(version: 2019_12_03_142803) do
14 |
15 | # These are extensions that must be enabled in order to support this database
16 | enable_extension "plpgsql"
17 |
18 | create_table "messages", force: :cascade do |t|
19 | t.string "content"
20 | t.string "author"
21 | t.datetime "created_at", precision: 6, null: false
22 | t.datetime "updated_at", precision: 6, null: false
23 | end
24 |
25 | create_table "previews", force: :cascade do |t|
26 | t.string "title"
27 | t.string "url"
28 | t.string "image_url"
29 | t.text "description"
30 | t.bigint "message_id", null: false
31 | t.datetime "created_at", precision: 6, null: false
32 | t.datetime "updated_at", precision: 6, null: false
33 | t.index ["message_id"], name: "index_previews_on_message_id"
34 | end
35 |
36 | add_foreign_key "previews", "messages"
37 | end
38 |
--------------------------------------------------------------------------------
/db/seeds.rb:
--------------------------------------------------------------------------------
1 | # This file should contain all the record creation needed to seed the database with its default values.
2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup).
3 | #
4 | # Examples:
5 | #
6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }])
7 | # Character.create(name: 'Luke', movie: movies.first)
8 |
--------------------------------------------------------------------------------
/dip.yml:
--------------------------------------------------------------------------------
1 | # Run eval "$(dip console)" in open terminal tab to not have to to prepend
2 | # commands with `dip`:
3 | # `dip rails c` -> `rails c`
4 |
5 | # Required minimum dip version
6 | version: "4.1"
7 |
8 | environment:
9 | COMPOSE_EXT: development
10 |
11 | compose:
12 | files:
13 | - docker-compose.yml
14 | project_name: rails-k8s-demo
15 |
16 | interaction:
17 | bash:
18 | description: Open the Bash shell in rails container
19 | service: app
20 | command: bash
21 | compose:
22 | run_options: [no-deps]
23 |
24 | bundle:
25 | description: Run Bundler commands
26 | service: app
27 | command: bundle
28 |
29 | yarn:
30 | description: Run Yarn commands
31 | service: app
32 | command: yarn
33 |
34 | rake:
35 | description: Run Rake commands
36 | service: app
37 | command: bundle exec rake
38 |
39 | standardrb:
40 | description: Run standardrb on Project
41 | service: app
42 | command: bundle exec standardrb
43 |
44 | rspec:
45 | description: Run Rspec commands
46 | service: app
47 | environment:
48 | RAILS_ENV: test
49 | command: ./vnc.sh bundle exec rspec --format=documentation
50 | compose:
51 | run_options: ["publish=5900:5900"]
52 |
53 | sidekiq:
54 | description: Run commands in sidekiq container
55 | service: sidekiq
56 | compose:
57 | method: run
58 | subcommands:
59 | logs:
60 | description: "Display last 200 lines of Sidekiq logs and follow"
61 | compose:
62 | method: logs
63 | run_options: [follow, tail='200']
64 |
65 | rails:
66 | description: Run Rails commands
67 | service: app
68 | command: bundle exec rails
69 | subcommands:
70 | s:
71 | description: Run Rails server at http://localhost:3000
72 | service: rails
73 | compose:
74 | run_options: [service-ports, use-aliases]
75 | s-altport:
76 | description: Run Rails server at select port `dip run -p 5000:3000 rails s-altport`
77 | service: rails
78 | compose:
79 | run_options: [use-aliases]
80 | logs:
81 | description: Display last 200 lines of Rails logs and follow
82 | service: rails
83 | compose:
84 | method: logs
85 | run_options: [follow, tail='200']
86 |
87 | webpacker:
88 | description: Run commands towards Webpacker service
89 | service: webpacker
90 | subcommands:
91 | logs:
92 | description: Display last 200 lines of Webpacker logs and follow
93 | compose:
94 | method: logs
95 | run_options: [follow, tail='200']
96 | compose:
97 | run_options: [service-ports, use-aliases]
98 |
99 | psql:
100 | description: Run Postgres psql console
101 | service: app
102 | default_args: forms_development
103 | command: psql -h postgres -U postgres
104 |
105 | provision:
106 | - docker-compose down --volumes
107 | - docker-compose up -d postgres redis
108 | - docker-compose up -d --build app
109 | - dip bundle install
110 | - dip yarn install
111 | - "dip rails db:prepare"
112 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.4"
2 |
3 | # General config for app, rails, sidekiq, webpacker
4 | # Magic from https://evilmartians.com/chronicles/ruby-on-whales-docker-for-ruby-rails-development
5 |
6 | x-template-app: &template-app
7 | build:
8 | context: .
9 | dockerfile: Dockerfile
10 | args:
11 | BUNDLER_VERSION: "2.1.4"
12 | image: rails-k8s-demo:dev
13 | tmpfs:
14 | - /tmp
15 | stdin_open: true
16 | tty: true
17 | volumes:
18 | - .:/app:cached
19 | - rails_cache:/app/tmp/cache
20 | - bundle:/usr/local/bundle
21 | - node_modules:/app/node_modules
22 | - packs:/app/public/packs
23 | - .psqlrc:/root/.psqlrc:ro
24 | environment:
25 | - NODE_ENV=development
26 | - RAILS_ENV=${RAILS_ENV:-development}
27 | - REDIS_URL=redis://redis:6379/
28 | - DATABASE_URL=postgres://postgres:postgres@postgres:5432
29 | - BOOTSNAP_CACHE_DIR=/usr/local/bundle/bootsnap
30 | - WEBPACKER_DEV_SERVER_HOST=webpacker
31 | - WEB_CONCURRENCY=1
32 | - HISTFILE=/app/log/.bash_history
33 | - PSQL_HISTFILE=/app/log/.psql_history
34 | - EDITOR=vi
35 |
36 | services:
37 | app: &app
38 | <<: *template-app
39 | command: irb
40 | depends_on:
41 | - postgres
42 | - redis
43 |
44 | rails:
45 | <<: *template-app
46 | command:
47 | - "bash"
48 | - "-c"
49 | - >
50 | rm -f /app/tmp/pids/server.pid &&
51 | bundle exec rails server -b 0.0.0.0
52 | ports:
53 | - "3000:3000"
54 | depends_on:
55 | - app
56 |
57 | sidekiq:
58 | <<: *template-app
59 | command: bundle exec sidekiq -C config/sidekiq.yml
60 | depends_on:
61 | - app
62 |
63 | webpacker:
64 | <<: *template-app
65 | command: ./bin/webpack-dev-server
66 | ports:
67 | - "3035:3035"
68 | volumes:
69 | - .:/app:cached
70 | - bundle:/usr/local/bundle
71 | - node_modules:/app/node_modules
72 | - packs:/app/public/packs
73 | environment:
74 | - WEBPACKER_DEV_SERVER_HOST=0.0.0.0
75 | - NODE_ENV=${NODE_ENV:-development}
76 | - RAILS_ENV=${RAILS_ENV:-development}
77 |
78 | postgres:
79 | image: postgres:11.1
80 | volumes:
81 | - .psqlrc:/root/.psqlrc:ro
82 | - postgres:/var/lib/postgresql/data
83 | - ./log:/root/log:cached
84 | environment:
85 | - PSQL_HISTFILE=/root/log/.psql_history
86 | ports:
87 | - 5432
88 |
89 | redis:
90 | image: redis:4.0.14-alpine
91 | volumes:
92 | - redis:/data
93 | ports:
94 | - 6379
95 |
96 | volumes:
97 | postgres:
98 | redis:
99 | bundle:
100 | node_modules:
101 | rails_cache:
102 | packs:
103 |
--------------------------------------------------------------------------------
/docs/app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/docs/app.png
--------------------------------------------------------------------------------
/docs/processes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/docs/processes.png
--------------------------------------------------------------------------------
/docs/secrets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/docs/secrets.png
--------------------------------------------------------------------------------
/helm/Chart.lock:
--------------------------------------------------------------------------------
1 | dependencies:
2 | - name: redis
3 | repository: https://kubernetes-charts.storage.googleapis.com/
4 | version: 10.2.1
5 | digest: sha256:c84f28b553f023b9b9afbb4ec56152545cbc70b228d9981fec4f9ae01b5838b4
6 | generated: "2020-06-17T21:58:23.613426+02:00"
7 |
--------------------------------------------------------------------------------
/helm/Chart.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v2
2 | name: rails-k8s-demo
3 | description: A Helm chart for Kubernetes
4 | type: application
5 | version: 0.1.0
6 | appVersion: 1.0
7 |
8 | dependencies:
9 | - name: redis
10 | version: 10.2.1
11 | repository: "@stable"
12 |
--------------------------------------------------------------------------------
/helm/charts/redis-10.2.1.tgz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/helm/charts/redis-10.2.1.tgz
--------------------------------------------------------------------------------
/helm/templates/NOTES.txt:
--------------------------------------------------------------------------------
1 | 1. Get the application URL by running these commands:
2 | {{- if .Values.ingress.enabled }}
3 | {{- range $host := .Values.ingress.hosts }}
4 | {{- range .paths }}
5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
6 | {{- end }}
7 | {{- end }}
8 | {{- else if contains "NodePort" .Values.service.type }}
9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "rails-k8s-demo.fullname" . }})
10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
11 | echo http://$NODE_IP:$NODE_PORT
12 | {{- else if contains "LoadBalancer" .Values.service.type }}
13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available.
14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "rails-k8s-demo.fullname" . }}'
15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "rails-k8s-demo.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
16 | echo http://$SERVICE_IP:{{ .Values.service.port }}
17 | {{- else if contains "ClusterIP" .Values.service.type }}
18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "rails-k8s-demo.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
19 | echo "Visit http://127.0.0.1:8080 to use your application"
20 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:80
21 | {{- end }}
22 |
--------------------------------------------------------------------------------
/helm/templates/_helpers.tpl:
--------------------------------------------------------------------------------
1 | {{/* vim: set filetype=mustache: */}}
2 | {{/*
3 | Expand the name of the chart.
4 | */}}
5 | {{- define "rails-k8s-demo.name" -}}
6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
7 | {{- end -}}
8 |
9 | {{/*
10 | Create a default fully qualified app name.
11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
12 | If release name contains chart name it will be used as a full name.
13 | */}}
14 | {{- define "rails-k8s-demo.fullname" -}}
15 | {{- if .Values.fullnameOverride -}}
16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
17 | {{- else -}}
18 | {{- $name := default .Chart.Name .Values.nameOverride -}}
19 | {{- if contains $name .Release.Name -}}
20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}}
21 | {{- else -}}
22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
23 | {{- end -}}
24 | {{- end -}}
25 | {{- end -}}
26 |
27 | {{/*
28 | Create chart name and version as used by the chart label.
29 | */}}
30 | {{- define "rails-k8s-demo.chart" -}}
31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
32 | {{- end -}}
33 |
34 | {{/*
35 | Common labels
36 | */}}
37 | {{- define "rails-k8s-demo.labels" -}}
38 | helm.sh/chart: {{ include "rails-k8s-demo.chart" . }}
39 | {{ include "rails-k8s-demo.selectorLabels" . }}
40 | {{- if .Chart.AppVersion }}
41 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
42 | {{- end }}
43 | app.kubernetes.io/managed-by: {{ .Release.Service }}
44 | {{- end -}}
45 |
46 | {{/*
47 | Selector labels
48 | */}}
49 | {{- define "rails-k8s-demo.selectorLabels" -}}
50 | app.kubernetes.io/name: {{ include "rails-k8s-demo.name" . }}
51 | app.kubernetes.io/instance: {{ .Release.Name }}
52 | {{- end -}}
53 |
54 | {{/*
55 | Create the name of the service account to use
56 | */}}
57 | {{- define "rails-k8s-demo.serviceAccountName" -}}
58 | {{- if .Values.serviceAccount.create -}}
59 | {{ default (include "rails-k8s-demo.fullname" .) .Values.serviceAccount.name }}
60 | {{- else -}}
61 | {{ default "default" .Values.serviceAccount.name }}
62 | {{- end -}}
63 | {{- end -}}
64 |
--------------------------------------------------------------------------------
/helm/templates/deployments/rails.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{ include "rails-k8s-demo.fullname" . }}-rails
5 | labels:
6 | {{- include "rails-k8s-demo.labels" . | nindent 4 }}
7 | spec:
8 | replicas: {{ .Values.rails.replicaCount }}
9 | selector:
10 | matchLabels:
11 | {{- include "rails-k8s-demo.selectorLabels" . | nindent 6 }}
12 | app.kubernetes.io/component: web
13 | template:
14 | metadata:
15 | labels:
16 | {{- include "rails-k8s-demo.selectorLabels" . | nindent 8 }}
17 | app.kubernetes.io/component: web
18 | spec:
19 | {{- with .Values.imagePullSecrets }}
20 | imagePullSecrets:
21 | {{- toYaml . | nindent 8 }}
22 | {{- end }}
23 | serviceAccountName: {{ include "rails-k8s-demo.serviceAccountName" . }}
24 | securityContext:
25 | {{- toYaml .Values.podSecurityContext | nindent 8 }}
26 | containers:
27 | - name: {{ .Chart.Name }}
28 | securityContext:
29 | {{- toYaml .Values.securityContext | nindent 12 }}
30 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
31 | imagePullPolicy: {{ .Values.image.pullPolicy }}
32 | ports:
33 | - name: http
34 | containerPort: 3000
35 | protocol: TCP
36 | command:
37 | - rails
38 | args:
39 | - "server"
40 | - "-b"
41 | - "0.0.0.0"
42 | # TODO: Figure out how to query the actual pod
43 | # livenessProbe:
44 | # httpGet:
45 | # scheme: HTTPS
46 | # host: demo.lewagon.co
47 | # path: /
48 | # port: 443
49 | # timeoutSeconds: 5
50 | # failureThreshold: 3
51 | # periodSeconds: 5
52 | # readinessProbe:
53 | # httpGet:
54 | # scheme: HTTPS
55 | # host: demo.lewagon.co
56 | # path: /
57 | # port: 443
58 | # timeoutSeconds: 5
59 | # failureThreshold: 3
60 | # initialDelaySeconds: 30
61 | envFrom:
62 | - configMapRef:
63 | name: common-env
64 | {{if .Values.dbConnectionString }}
65 | env:
66 | - name: DATABASE_URL
67 | valueFrom:
68 | secretKeyRef:
69 | name: db-connection-string
70 | key: db_connection_string
71 | {{ end }}
72 | {{if .Values.railsMasterKey }}
73 | env:
74 | - name: RAILS_MASTER_KEY
75 | valueFrom:
76 | secretKeyRef:
77 | name: rails-master-key
78 | key: rails_master_key
79 | {{ end }}
80 | resources:
81 | {{- toYaml .Values.resources | nindent 12 }}
82 | {{- with .Values.nodeSelector }}
83 | nodeSelector:
84 | {{- toYaml . | nindent 8 }}
85 | {{- end }}
86 | {{- with .Values.affinity }}
87 | affinity:
88 | {{- toYaml . | nindent 8 }}
89 | {{- end }}
90 | {{- with .Values.tolerations }}
91 | tolerations:
92 | {{- toYaml . | nindent 8 }}
93 | {{- end }}
94 |
--------------------------------------------------------------------------------
/helm/templates/deployments/sidekiq.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: {{ include "rails-k8s-demo.fullname" . }}-sidekiq
5 | labels:
6 | {{ include "rails-k8s-demo.labels" . | indent 4 }}
7 | spec:
8 | replicas: {{ .Values.sidekiq.replicaCount }}
9 | selector:
10 | matchLabels:
11 | {{- include "rails-k8s-demo.selectorLabels" . | nindent 6 }}
12 | app.kubernetes.io/component: background
13 | template:
14 | metadata:
15 | labels:
16 | {{- include "rails-k8s-demo.selectorLabels" . | nindent 8 }}
17 | app.kubernetes.io/component: background
18 | spec:
19 | {{- with .Values.imagePullSecrets }}
20 | imagePullSecrets:
21 | {{- toYaml . | nindent 8 }}
22 | {{- end }}
23 | containers:
24 | - name: {{ .Chart.Name }}
25 | securityContext:
26 | {{- toYaml .Values.securityContext | nindent 12 }}
27 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
28 | imagePullPolicy: {{ .Values.image.pullPolicy }}
29 | ports:
30 | - name: sidekiqalive
31 | containerPort: 7433
32 | command:
33 | - bundle
34 | - exec
35 | - sidekiq
36 | args:
37 | - "-C"
38 | - "config/sidekiq.yml"
39 | livenessProbe:
40 | httpGet:
41 | path: /
42 | port: 7433
43 | initialDelaySeconds: 30
44 | readinessProbe:
45 | httpGet:
46 | path: /
47 | port: 7433
48 | initialDelaySeconds: 30 # app specific. Time your sidekiq takes to start processing.
49 | envFrom:
50 | - configMapRef:
51 | name: common-env
52 | {{if .Values.dbConnectionString }}
53 | env:
54 | - name: DATABASE_URL
55 | valueFrom:
56 | secretKeyRef:
57 | name: db-connection-string
58 | key: db_connection_string
59 | {{ end }}
60 | {{if .Values.railsMasterKey }}
61 | env:
62 | - name: RAILS_MASTER_KEY
63 | valueFrom:
64 | secretKeyRef:
65 | name: rails-master-key
66 | key: rails_master_key
67 | {{ end }}
68 | resources:
69 | {{- toYaml .Values.resources | nindent 12 }}
70 | # Dafault helm
71 | {{- with .Values.nodeSelector }}
72 | nodeSelector:
73 | {{- toYaml . | nindent 8 }}
74 | {{- end }}
75 | {{- with .Values.affinity }}
76 | affinity:
77 | {{- toYaml . | nindent 8 }}
78 | {{- end }}
79 | {{- with .Values.tolerations }}
80 | tolerations:
81 | {{- toYaml . | nindent 8 }}
82 | {{- end }}
83 |
--------------------------------------------------------------------------------
/helm/templates/environment/common-env.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: common-env
5 | annotations:
6 | helm.sh/hook: pre-upgrade, pre-install
7 | helm.sh/hook-weight: "-1"
8 | data:
9 | REDIS_URL: redis://{{ .Release.Name }}-redis-master.default.svc.cluster.local
10 | {{- range $key, $value := .Values.commonEnv }}
11 | {{ $key }}: {{ $value | quote }}
12 | {{- end }}
13 |
--------------------------------------------------------------------------------
/helm/templates/ingress.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.ingress.enabled -}}
2 | {{- $fullName := include "rails-k8s-demo.fullname" . -}}
3 | {{- $svcPort := .Values.service.port -}}
4 | {{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
5 | apiVersion: networking.k8s.io/v1beta1
6 | {{- else -}}
7 | apiVersion: extensions/v1beta1
8 | {{- end }}
9 | kind: Ingress
10 | metadata:
11 | name: {{ $fullName }}
12 | labels:
13 | {{- include "rails-k8s-demo.labels" . | nindent 4 }}
14 | {{- with .Values.ingress.annotations }}
15 | annotations:
16 | {{- toYaml . | nindent 4 }}
17 | {{- end }}
18 | spec:
19 | {{- if .Values.ingress.tls }}
20 | tls:
21 | {{- range .Values.ingress.tls }}
22 | - hosts:
23 | {{- range .hosts }}
24 | - {{ . | quote }}
25 | {{- end }}
26 | secretName: {{ .secretName }}
27 | {{- end }}
28 | {{- end }}
29 | rules:
30 | {{- range .Values.ingress.hosts }}
31 | - host: {{ .host | quote }}
32 | http:
33 | paths:
34 | {{- range .paths }}
35 | - path: {{ . }}
36 | backend:
37 | serviceName: {{ $fullName }}
38 | servicePort: {{ $svcPort }}
39 | {{- end }}
40 | {{- end }}
41 | {{- end }}
42 |
--------------------------------------------------------------------------------
/helm/templates/jobs/db-prepare.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: batch/v1
2 | kind: Job
3 | metadata:
4 | name: db-prepare
5 | annotations:
6 | helm.sh/hook: pre-upgrade, pre-install
7 | helm.sh/hook-delete-policy: hook-succeeded, hook-failed
8 | spec:
9 | activeDeadlineSeconds: 180
10 | backoffLimit: 3
11 | template:
12 | spec:
13 | restartPolicy: Never
14 | containers:
15 | - name: db-prepare
16 | image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
17 | imagePullPolicy: {{ .Values.image.pullPolicy }}
18 | command:
19 | - bundle
20 | - exec
21 | - rails
22 | - db:prepare
23 | {{if .Values.dbConnectionString }}
24 | env:
25 | - name: DATABASE_URL
26 | valueFrom:
27 | secretKeyRef:
28 | name: db-connection-string
29 | key: db_connection_string
30 | {{ end }}
31 |
--------------------------------------------------------------------------------
/helm/templates/secrets/db-connection-secret.yaml:
--------------------------------------------------------------------------------
1 | {{ if .Values.dbConnectionString }}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: db-connection-string
6 | annotations:
7 | helm.sh/hook: pre-upgrade, pre-install
8 | helm.sh/hook-weight: "-2"
9 | type: Opaque
10 | stringData:
11 | db_connection_string: {{ .Values.dbConnectionString }}
12 | {{ end }}
13 |
--------------------------------------------------------------------------------
/helm/templates/secrets/master-key-secret.yaml:
--------------------------------------------------------------------------------
1 | {{ if .Values.railsMasterKey }}
2 | apiVersion: v1
3 | kind: Secret
4 | metadata:
5 | name: rails-master-key
6 | annotations:
7 | helm.sh/hook: pre-upgrade, pre-install
8 | helm.sh/hook-weight: "-2"
9 | type: Opaque
10 | stringData:
11 | rails_master_key: {{ .Values.railsMasterKey }}
12 | {{ end }}
13 |
--------------------------------------------------------------------------------
/helm/templates/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: {{ include "rails-k8s-demo.fullname" . }}
5 | labels: {{- include "rails-k8s-demo.labels" . | nindent 4 }}
6 | spec:
7 | type: {{ .Values.service.type }}
8 | ports:
9 | - port: {{ .Values.service.port }}
10 | targetPort: 3000
11 | protocol: TCP
12 | name: http
13 | selector:
14 | app.kubernetes.io/name: {{ include "rails-k8s-demo.name" . }}
15 | app.kubernetes.io/instance: {{ .Release.Name }}
16 | app.kubernetes.io/component: web
17 |
--------------------------------------------------------------------------------
/helm/templates/serviceaccount.yaml:
--------------------------------------------------------------------------------
1 | {{- if .Values.serviceAccount.create -}}
2 | apiVersion: v1
3 | kind: ServiceAccount
4 | metadata:
5 | name: {{ include "rails-k8s-demo.serviceAccountName" . }}
6 | labels:
7 | {{ include "rails-k8s-demo.labels" . | nindent 4 }}
8 | {{- end -}}
9 |
--------------------------------------------------------------------------------
/helm/templates/ssl/cert.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1alpha2
2 | kind: Certificate
3 | metadata:
4 | name: tls-cert
5 | spec:
6 | secretName: tls-cert
7 |
8 | dnsNames:
9 | {{- range .Values.ingress.hosts }}
10 | - {{ .host | quote }}
11 | {{- end }}
12 |
13 | issuerRef:
14 | name: letsencrypt-prod
15 | kind: ClusterIssuer
16 | group: cert-manager.io
17 |
--------------------------------------------------------------------------------
/helm/templates/ssl/production-issuer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1alpha2
2 | kind: ClusterIssuer
3 | metadata:
4 | name: letsencrypt-prod
5 | spec:
6 | acme:
7 | # Email address used for ACME registration
8 | email: andrey@lewagon.org # TODO: move to values
9 | server: https://acme-v02.api.letsencrypt.org/directory
10 | privateKeySecretRef:
11 | # Name of a secret used to store the ACME account private key
12 | name: letsencrypt-prod
13 | # Add a single challenge solver, HTTP01 using nginx
14 | solvers:
15 | - http01:
16 | ingress:
17 | class: nginx
18 |
--------------------------------------------------------------------------------
/helm/templates/tests/test-connection.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: "{{ include "rails-k8s-demo.fullname" . }}-test-connection"
5 | labels:
6 | {{ include "rails-k8s-demo.labels" . | nindent 4 }}
7 | annotations:
8 | "helm.sh/hook": test-success
9 | spec:
10 | containers:
11 | - name: wget
12 | image: busybox
13 | command: ['wget']
14 | args: ['{{ include "rails-k8s-demo.fullname" . }}:{{ .Values.service.port }}']
15 | restartPolicy: Never
16 |
--------------------------------------------------------------------------------
/helm/values.yaml:
--------------------------------------------------------------------------------
1 | image:
2 | repository: quay.io/lewagon/rails-k8s-demo
3 | tag: latest
4 | pullPolicy: Always # TODO: change to IfNotPresent after debug?
5 |
6 | imagePullSecrets: []
7 | nameOverride: ""
8 | fullnameOverride: ""
9 |
10 | rails:
11 | replicaCount: 1
12 |
13 | sidekiq:
14 | replicaCount: 1
15 |
16 | # ENV variables declared here will be set for both Rails and Sidekiq pods
17 | commonEnv:
18 | # ENV_VAR: value
19 |
20 | # Values used to generate secrets at chart install/upgrade.
21 | # These values need to be injected through shell env/CI secrets,
22 | # Don't set them here and don't commit to source control.
23 | dbConnectionString:
24 | railsMasterKey:
25 |
26 | serviceAccount:
27 | # Specifies whether a service account should be created
28 | create: true
29 | # The name of the service account to use.
30 | # If not set and create is true, a name is generated using the fullname template
31 | name:
32 |
33 | podSecurityContext:
34 | {}
35 | # fsGroup: 2000
36 |
37 | securityContext:
38 | {}
39 | # capabilities:
40 | # drop:
41 | # - ALL
42 | # readOnlyRootFilesystem: true
43 | # runAsNonRoot: true
44 | # runAsUser: 1000
45 |
46 | service:
47 | type: ClusterIP
48 | port: 80
49 |
50 | ingress:
51 | enabled: true
52 | annotations:
53 | kubernetes.io/ingress.class: nginx
54 | certmanager.io/cluster-issuer: letsencrypt-prod
55 | hosts:
56 | - host: demo.lewagon.co
57 | paths: ["/"]
58 |
59 | tls:
60 | - secretName: tls-cert
61 | hosts:
62 | - demo.lewagon.co
63 |
64 | resources:
65 | {}
66 | # We usually recommend not to specify default resources and to leave this as a conscious
67 | # choice for the user. This also increases chances charts run on environments with little
68 | # resources, such as Minikube. If you do want to specify resources, uncomment the following
69 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
70 | # limits:
71 | # cpu: 100m
72 | # memory: 128Mi
73 | # requests:
74 | # cpu: 100m
75 | # memory: 128Mi
76 |
77 | nodeSelector: {}
78 |
79 | tolerations: []
80 |
81 | affinity: {}
82 |
83 | redis:
84 | cluster:
85 | enabled: false
86 | usePassword: false
87 | # By default, FLUSH and FLUSHALL are disabled in bitnami/redis chart
88 | master:
89 | disableCommands: []
90 |
--------------------------------------------------------------------------------
/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/lib/assets/.keep
--------------------------------------------------------------------------------
/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/lib/tasks/.keep
--------------------------------------------------------------------------------
/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/log/.keep
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "review_scraper",
3 | "private": true,
4 | "dependencies": {
5 | "@rails/actioncable": "^6.0.0-alpha",
6 | "@rails/ujs": "^6.0.0-alpha",
7 | "@rails/webpacker": "^4.0.7",
8 | "bootstrap": "4.3.1",
9 | "dot-prop": "5.1.1",
10 | "jquery": "^3.5.0",
11 | "minimist": "^1.2.3",
12 | "popper.js": "^1.16.0",
13 | "stimulus": "^1.1.1",
14 | "turbolinks": "^5.2.0"
15 | },
16 | "version": "0.1.0",
17 | "devDependencies": {
18 | "webpack-dev-server": "^3.9.0"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: [
3 | require('postcss-import'),
4 | require('postcss-flexbugs-fixes'),
5 | require('postcss-preset-env')({
6 | autoprefixer: {
7 | flexbox: 'no-2009'
8 | },
9 | stage: 3
10 | })
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/public/apple-touch-icon-precomposed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/public/apple-touch-icon-precomposed.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/public/favicon.ico
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 |
--------------------------------------------------------------------------------
/test/application_system_test_case.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5 | end
6 |
--------------------------------------------------------------------------------
/test/channels/application_cable/connection_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase
4 | # test "connects with cookies" do
5 | # cookies.signed[:user_id] = 42
6 | #
7 | # connect
8 | #
9 | # assert_equal connection.user_id, "42"
10 | # end
11 | end
12 |
--------------------------------------------------------------------------------
/test/channels/messages_channel_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class MessagesChannelTest < ActionCable::Channel::TestCase
4 | # test "subscribes" do
5 | # subscribe
6 | # assert subscription.confirmed?
7 | # end
8 | end
9 |
--------------------------------------------------------------------------------
/test/controllers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/controllers/.keep
--------------------------------------------------------------------------------
/test/controllers/logins_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class LoginControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/controllers/messages_controller_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class MessagesControllerTest < ActionDispatch::IntegrationTest
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/fixtures/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/fixtures/.keep
--------------------------------------------------------------------------------
/test/fixtures/files/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/fixtures/files/.keep
--------------------------------------------------------------------------------
/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/helpers/.keep
--------------------------------------------------------------------------------
/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/integration/.keep
--------------------------------------------------------------------------------
/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/mailers/.keep
--------------------------------------------------------------------------------
/test/models/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/models/.keep
--------------------------------------------------------------------------------
/test/models/message_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class MessageTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/models/preview_test.rb:
--------------------------------------------------------------------------------
1 | require "test_helper"
2 |
3 | class PreviewTest < ActiveSupport::TestCase
4 | # test "the truth" do
5 | # assert true
6 | # end
7 | end
8 |
--------------------------------------------------------------------------------
/test/system/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/test/system/.keep
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV["RAILS_ENV"] ||= "test"
2 | require_relative "../config/environment"
3 | require "rails/test_help"
4 |
5 | class ActiveSupport::TestCase
6 | # Run tests in parallel with specified workers
7 | parallelize(workers: :number_of_processors)
8 |
9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
10 | fixtures :all
11 |
12 | # Add more helper methods to be used by all tests here...
13 | end
14 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/tmp/.keep
--------------------------------------------------------------------------------
/vendor/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lewagon/rails-k8s-demo/e2f5cf697d86811b52cb0b9045d37cbe0d02dda6/vendor/.keep
--------------------------------------------------------------------------------