Skip to content

Commit ee71ea2

Browse files
committed
Improve the docker-compose development environment
Signed-off-by: Miquel Sabaté Solà <[email protected]>
1 parent 36cce88 commit ee71ea2

File tree

8 files changed

+163
-50
lines changed

8 files changed

+163
-50
lines changed

.env.example

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
SECRET_KEY_BASE=8ea53ad3bc6c03923e376c8bdd85059c1885524947a7efe53d5e9c9d4e39861106ffd6a2ece82b803072ed701e6c960bade91644979e679416c5f255007237ae
12
EXTERNAL_PORT=8080
23
SCC_USERNAME=
34
SCC_PASSWORD=

Dockerfile

+9-28
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
FROM opensuse/leap:15.2
1+
FROM opensuse/leap:15.3
22

3-
RUN zypper --non-interactive install --no-recommends timezone wget gcc-c++ libffi-devel git-core zlib-devel \
4-
libxml2-devel libxslt-devel cron libmariadb-devel mariadb-client vim ruby2.5 ruby2.5-devel ruby2.5-rubygem-bundler &&\
5-
zypper --non-interactive install -t pattern devel_basis
6-
7-
ENV DOCKERIZE_VERSION v0.6.0
8-
RUN wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
9-
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
10-
&& rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
11-
12-
ENV RAILS_ENV production
3+
RUN zypper --non-interactive install --no-recommends \
4+
timezone wget gcc-c++ libffi-devel git-core zlib-devel \
5+
libxml2-devel libxslt-devel cron libmariadb-devel mariadb-client \
6+
vim ruby2.5 ruby2.5-devel ruby2.5-rubygem-bundler SUSEConnect && \
7+
zypper --non-interactive install -t pattern devel_basis && \
8+
update-alternatives --install /usr/bin/bundle bundle /usr/bin/bundle.ruby2.5 5 && \
9+
update-alternatives --install /usr/bin/bundler bundler /usr/bin/bundler.ruby2.5 5
1310

1411
WORKDIR /srv/www/rmt/
1512

@@ -28,22 +25,6 @@ RUN sed -i 's/#!\/usr\/bin\/env ruby/#!\/usr\/bin\/ruby.ruby2.5/g' /srv/www/rmt/
2825
chown _rmt /srv/www/rmt/public/repo && \
2926
chown _rmt /srv/www/rmt/public/suma
3027

31-
RUN printf "database: &database\n\
32-
host: <%%= ENV['MYSQL_HOST'] %%>\n\
33-
username: <%%= ENV['MYSQL_USER'] %%>\n\
34-
password: <%%= ENV['MYSQL_PASSWORD'] %%>\n\
35-
database: <%%= ENV['MYSQL_DATABASE'] %%>\n\
36-
database_development:\n\
37-
<<: *database\n\
38-
database: <%%= ENV['MYSQL_DATABASE'] %%>\n\
39-
cli:\n\
40-
user: root\n\
41-
group: root\n\
42-
scc:\n\
43-
username: <%%= ENV['SCC_USERNAME'] %%>\n\
44-
password: <%%= ENV['SCC_PASSWORD'] %%>\n\
45-
" >> config/rmt.local.yml
46-
4728
EXPOSE 4224
4829

49-
CMD dockerize -wait tcp://$MYSQL_HOST:3306 -timeout 60s true && bundle.ruby2.5 exec rails s -b 0.0.0.0 -p 4224
30+
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0", "-p", "4224"]

README.md

+49-10
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,55 @@ Please view our [guide](docs/installation.md) to assist you in the RMT installat
6666
In order to run the application locally using docker-compose:
6767
6868
1. Copy the `.env.example` file to `.env`.
69-
2. Add your organization credentials to `.env` file. Mirroring credentials can be obtained from the [SUSE Customer Center](https://scc.suse.com/organization).
70-
3. Start `docker-compose` and build the containers:
71-
```
72-
docker-compose up --build
73-
```
74-
4. The web server is accessible at [http://localhost:8080/](http://localhost:8080/). This URL can be used for registering clients.
75-
5. To start a shell inside the RMT docker container, run the following command:
76-
```
77-
docker-compose exec rmt bash
78-
```
69+
2. Add your organization credentials to `.env` file. Mirroring credentials can
70+
be obtained from the [SUSE Customer
71+
Center](https://scc.suse.com/organization). At this point you might also want
72+
to tweak the `EXTERNAL_PORT` environment variable from this file if you want
73+
to expose the main service with a port different than from the default one.
74+
3. Change the permissions on the `public` folder so anyone can access it (i.e.
75+
`chmod -R 0777 public`). This is needed so the docker container can write
76+
into this specific directory which is protected by default by the `rmt-cli`
77+
tool.
78+
4. Build the containers needed by `docker-compose`:
79+
```
80+
docker-compose build
81+
```
82+
5. Run everything with `docker-compose up`.
83+
84+
After doing all this, there will be `http://localhost:${EXTERNAL_PORT}` exposed
85+
to the network of the host, and you will be able to register clients by using
86+
this url. At this point, though, notice that there are two ways to run clients
87+
in a dockerized fashion as well.
88+
89+
First of all, you can run a client from a custom container (e.g. generated from
90+
the `SUSE/connect` repository). With this in mind, be aware that you need to be
91+
on the same network namespace as the host (or the `docker-compose` setup). You
92+
can manage this with the `--network` flag of the `docker run` command. One easy
93+
way to achieve this is to set the network as the one from the host: `docker run
94+
--network=host <...>`. Moreover, notice that `dmidecode`, which is run by
95+
`SUSEConnect`, will try to access some privileged devices (e.g. `/dev/mem`). By
96+
default this will also fail, which is why some users simply pass the
97+
`--privileged` flag to workaround this. This is certainly a solution, but it's
98+
cleaner to simply add the needed capabilities and the needed devices. In
99+
conclusion, for a clean run of a client, you could run the following command:
100+
101+
``` sh
102+
$ docker run --rm --network=host --cap-add=sys_rawio --device /dev/mem:/dev/mem -ti <your-docker-image> /bin/bash
103+
> SUSEConnect -r <regcode> --url http://localhost:${EXTERNAL_PORT}
104+
```
105+
106+
Another option is to simply attach a new session into the running `rmt` service,
107+
which already has the needed devices and capabilities. Thus, you could do
108+
something like this:
109+
110+
``` sh
111+
$ docker-compose exec rmt /bin/bash
112+
> SUSEConnect -r <regcode> --url http://localhost:${EXTERNAL_PORT}
113+
```
114+
115+
All in all, the code you might be working on sits as a volume inside of the
116+
Docker container. Thus, you will be able to code as usual and the Docker
117+
container will behave as if you were working entirely locally.
79118

80119
## API documentation
81120

bin/compose-init.rb

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# frozen_string_literal: true
2+
3+
begin
4+
::RMT::Db.setup!
5+
rescue ::RMT::Db::TimeoutReachedError
6+
Rails.logger.error "Database connection timed out!"
7+
exit 1
8+
end
9+
10+
# Create the `/var/lib/rmt/system_uuid` file if it does not exist already. This
11+
# will ease up the first run of `rmt-cli` inside of this container.
12+
unless File.exist?('/var/lib/rmt/system_uuid')
13+
system('dmidecode -s system-uuid > /var/lib/rmt/system_uuid')
14+
end
15+
16+
system('bundle exec rails s -b rmt -p 4224')

config/environments/development.rb

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require_relative '../../lib/rmt/logger'
2+
13
Rails.application.configure do
24
# Settings specified here will take precedence over those in config/application.rb.
35

@@ -34,6 +36,7 @@
3436
# Highlight code that triggered database queries in logs.
3537
config.active_record.verbose_query_logs = true
3638

39+
config.logger = RMT::Logger.new($stdout)
3740

3841
# Raises error for missing translations.
3942
# config.action_view.raise_on_missing_translations = true

config/rmt.yml

+6-7
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,26 @@
33
# You can create rmt.local.yml to override settings in this file.
44

55
database: &database
6-
host: localhost
7-
database: rmt_development
8-
username: rmt
9-
password: rmt
6+
host: <%= ENV.fetch('MYSQL_HOST') { 'localhost' } %>
7+
database: <%= ENV.fetch('MYSQL_DATABASE') { 'rmt_development' } %>
8+
username: <%= ENV.fetch('MYSQL_USER') { 'rmt' } %>
9+
password: <%= ENV.fetch('MYSQL_PASSWORD') { 'rmt' } %>
1010
adapter: mysql2
1111
encoding: utf8
1212
timeout: 5000
1313
pool: 5
1414

1515
database_development:
1616
<<: *database
17-
database: rmt_development
1817

1918
database_test:
2019
<<: *database
2120
database: rmt_test
2221

2322
scc:
2423
host: https://scc.suse.com/connect
25-
username:
26-
password:
24+
username: <%= ENV['SCC_USERNAME'] %>
25+
password: <%= ENV['SCC_PASSWORD'] %>
2726
sync_systems: true
2827

2928
mirroring:

docker-compose.yml

+17-5
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,36 @@ volumes:
66
services:
77
db:
88
image: mariadb:10.2
9-
restart: always
9+
restart: unless-stopped
1010
volumes:
11-
- db_storage:/var/lib/mysql
11+
- mariadb:/var/lib/mysql
1212
extends:
1313
service: base
1414
file: docker-compose-base.yml
1515

1616
rmt:
1717
build: .
18+
restart: unless-stopped
1819
environment:
1920
- MYSQL_HOST=db
20-
- SECRET_KEY_BASE=8ea53ad3bc6c03923e376c8bdd85059c1885524947a7efe53d5e9c9d4e39861106ffd6a2ece82b803072ed701e6c960bade91644979e679416c5f255007237ae
21+
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
2122
- SCC_USERNAME=${SCC_USERNAME}
2223
- SCC_PASSWORD=${SCC_PASSWORD}
2324
volumes:
24-
- ./public:/srv/www/repo/public/
25+
- .:/srv/www/rmt
26+
- public:/srv/www/repo/public/
2527
depends_on:
2628
- db
2729
extends:
2830
service: base
2931
file: docker-compose-base.yml
30-
command: /bin/bash -c 'bundle.ruby2.5 exec rails db:migrate && bundle.ruby2.5 exec rails s -b rmt'
32+
# Needed if you want to run SUSEConnect from inside this container.
33+
devices:
34+
- "/dev/mem:/dev/mem"
35+
# Needed if you want to run SUSEConnect from inside this container.
36+
cap_add:
37+
- sys_rawio
38+
command: bundle exec rails runner /srv/www/rmt/bin/compose-init.rb
3139

3240
nginx:
3341
image: nginx:1.14
@@ -39,3 +47,7 @@ services:
3947
depends_on:
4048
- rmt
4149
entrypoint: /bin/bash -c 'cat /tmp/default.template | sed "s/\\\$$server_port/$EXTERNAL_PORT/g" > /etc/nginx/conf.d/default.conf && nginx -g "daemon off;"'
50+
51+
volumes:
52+
public:
53+
mariadb:

lib/rmt/db.rb

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# frozen_string_literal: true
2+
3+
module RMT
4+
# The DB module has useful methods for DB purposes. This is largely based on
5+
# SUSE/Portus.
6+
module Db
7+
WAIT_TIMEOUT = 90
8+
WAIT_INTERVAL = 5
9+
10+
# Pings the DB and returns a proper symbol depending on the situation:
11+
# * ready: the database has been created and initialized.
12+
# * empty: the database has been created but has not been initialized.
13+
# * missing: the database has not been created.
14+
# * down: cannot connect to the database.
15+
# * unknown: there has been an unexpected error.
16+
def self.ping
17+
::RMT::Db.migrations? ? :ready : :empty
18+
rescue ActiveRecord::NoDatabaseError
19+
:missing
20+
rescue Mysql2::Error
21+
:down
22+
rescue StandardError
23+
:unknown
24+
end
25+
26+
# Returns true if the migrations have been run. The implementation is pretty
27+
# trivial, but this gives us a nice way to test this module.
28+
def self.migrations?
29+
ActiveRecord::Base.connection
30+
return unless ActiveRecord::Base.connection.table_exists? 'schema_migrations'
31+
32+
!ActiveRecord::Base.connection.migration_context.needs_migration?
33+
end
34+
35+
def self.setup!
36+
count = 0
37+
38+
loop do
39+
case ::RMT::Db.ping
40+
when :down
41+
Rails.logger.info 'Database not ready yet. Waiting...'
42+
sleep WAIT_INTERVAL
43+
count += 5
44+
when :empty
45+
Rails.logger.info 'Database empty: migrating...'
46+
system('bundle exec rake db:migrate')
47+
when :missing
48+
Rails.logger.info 'Database missing: creating...'
49+
system('bundle exec rake db:create')
50+
when :ready
51+
Rails.logger.info 'Database ready!'
52+
break
53+
end
54+
55+
raise ::RMT::Db::TimeoutReachedError if count >= WAIT_TIMEOUT
56+
end
57+
end
58+
59+
# Raised if any timeout reached
60+
class TimeoutReachedError < RuntimeError; end
61+
end
62+
end

0 commit comments

Comments
 (0)