Compare commits

...

32 Commits
0.6.0 ... 0.6.4

Author SHA1 Message Date
vabene1111
e78323d214 Update docker-publish-latest.yml 2020-04-15 15:05:36 +02:00
vabene1111
d2e866dd74 cleanup/refactor workflows 2020-04-15 14:48:18 +02:00
vabene1111
76687ad5df testing multi platform deployment 2020-04-15 12:34:58 +02:00
vabene1111
dab77e8e4f imrpoved index redirect + fixed tests 2020-04-13 23:11:33 +02:00
vabene1111
0b250c71aa actually fixed action yml 2020-04-13 22:55:50 +02:00
vabene1111
571f670db0 fixed action intendations 2020-04-13 22:54:17 +02:00
vabene1111
4e9e628162 added ability to change default page 2020-04-13 22:52:02 +02:00
vabene1111
4f49b06704 user setting default ingredient unit 2020-04-13 22:37:50 +02:00
vabene1111
8eb0c36665 fixed action invalid yaml 2020-04-13 21:53:36 +02:00
vabene1111
6f69c09aca Merge pull request #55 from hakoerber/kubernetes-manifests
Kubernetes manifests
2020-04-13 21:51:58 +02:00
vabene1111
8e6f153882 Merge pull request #56 from tourn/clickable-links
Make links in recipe clickable
2020-04-13 21:50:25 +02:00
vabene1111
07183fd40f actions testing 2020-04-13 21:39:20 +02:00
tourn
04b7f0a398 Make links in recipe clickable 2020-04-13 21:27:22 +02:00
Hannes Körber
1735fda48f Add basic kubernetes manifest
Closes #50
2020-04-13 19:39:41 +02:00
Hannes Körber
1c9ea0eda7 Use relative links in README
See https://github.blog/2013-01-31-relative-links-in-markup-files/ for
more details.
2020-04-13 19:39:39 +02:00
vabene1111
83b5b6695c Update docker-release-publish.yml 2020-04-13 18:25:49 +02:00
vabene1111
342fb3c96d Update docker-release-publish.yml 2020-04-13 18:23:19 +02:00
vabene1111
b7a18466b5 testing multi plattform builds 2020-04-13 18:17:53 +02:00
vabene1111
0cdc4d51df Merge branch 'feature/webdav-root-option' into develop 2020-04-13 17:41:02 +02:00
vabene1111
e177669514 Merge pull request #51 from pataya23/develop
added 'webdav_root' option
2020-04-13 17:38:19 +02:00
pataya23
fd294dfcdd added 'webdav_root' option
otherwise I get an error (webdav3.exceptions.RemoteResourceNotFound: Remote resource: </path> not found)
2020-04-13 13:19:03 +02:00
vabene1111
bdd092e5d3 fixed ingredient amount numeric 2020-04-07 20:13:20 +02:00
vabene1111
f1c5a0ef5f character limit increased 2020-04-07 18:46:20 +02:00
vabene1111
1e8ff763d5 increased character limit keyword icon
thanks cazier for pointing out this issue and providing a PR https://github.com/vabene1111/recipes/pull/46
2020-04-07 18:46:09 +02:00
vabene1111
4cf6a3b219 Merge pull request #45 from tourn/improved-meal-plan
Add buttons to add a meal plan to a specific point in time
2020-04-05 14:47:55 +02:00
tourn
de145b6b18 Add some styling 2020-04-05 11:10:39 +02:00
vabene1111
84a8308bf3 updated tabulator js to 4.6 2020-03-31 01:20:49 +02:00
vabene1111
8d191fa1a1 set recipe name as page title 2020-03-31 01:11:14 +02:00
vabene1111
b47a0197e2 imroved recipe printing and view 2020-03-31 01:10:30 +02:00
tourn
4e7c5f9495 Add buttons to add a meal plan to a specific point in time 2020-03-30 22:06:17 +02:00
vabene1111
d704ddacdd fixed shopping list format 2020-03-27 21:47:00 +01:00
vabene1111
2c3140248c fixed broken links 2020-03-27 21:41:55 +01:00
29 changed files with 491 additions and 54 deletions

View File

@@ -1,18 +1,18 @@
name: Deploy Docker Image
name: publish dev image docker
on:
push:
tags:
- '*'
branches:
- '*'
- '*/*'
- '!master'
jobs:
build-push:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Publish to Registry
uses: AhnSeongHyun/action-tag-docker-build-push@v1.0.0
uses: elgohr/Publish-Docker-Github-Action@2.13
with:
repo_name: vabene1111/recipes
name: vabene1111/recipes
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -0,0 +1,19 @@
name: publish latest image docker
on:
push:
branches:
- master
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Build and publish image
uses: ilteoood/docker_buildx@master
with:
publish: true
imageName: vabene1111/recipes
tag: latest
dockerHubUser: ${{ secrets.DOCKER_USERNAME }}
dockerHubPassword: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -0,0 +1,25 @@
name: publish tagged release docker
on:
push:
tags:
- '*'
jobs:
build:
runs-on: ubuntu-latest
name: Build image job
steps:
- name: Checkout master
uses: actions/checkout@master#
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
- name: Build and publish image
uses: ilteoood/docker_buildx@master
with:
publish: true
imageName: vabene1111/recipes
tag: ${{ steps.get_version.outputs.VERSION }}
dockerHubUser: ${{ secrets.DOCKER_USERNAME }}
dockerHubPassword: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -1,13 +0,0 @@
name: Publish Docker
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Publish to Registry
uses: elgohr/Publish-Docker-Github-Action@2.13
with:
name: vabene1111/recipes
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

View File

@@ -25,7 +25,7 @@ The docker image (`vabene1111/recipes`) simply exposes the application on port `
### Docker-Compose
2. Choose one of the included configurations [here](https://github.com/vabene1111/recipes/tree/develop/docs/docker).
2. Choose one of the included configurations [here](docs/docker).
2. Download the environment (config) file template and fill it out `wget https://raw.githubusercontent.com/vabene1111/recipes/develop/.env.template -O .env `
3. Start the container `docker-compose up -d`
4. Create a default user by running `docker-compose exec web_recipes createsuperuser`.
@@ -46,6 +46,10 @@ While intermediate updates can be skipped when updating please make sure to **re
2. Pull the latest image using `docker-compose pull`
3. Start the container again using `docker-compose up -d`
## Kubernetes
You can find a basic kubernetes setup [here](docs/k8s/). Please see the README in the folder for more detail.
# Documentation
Most things should be straight forward but there are some more complicated things.

View File

@@ -31,10 +31,11 @@ class UserPreferenceForm(forms.ModelForm):
class Meta:
model = UserPreference
fields = ('theme', 'nav_color')
fields = ('default_unit', 'theme', 'nav_color', 'default_page')
help_texts = {
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!')
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.')
}

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.0.4 on 2020-04-07 16:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0030_recipeingredient_note'),
]
operations = [
migrations.AlterField(
model_name='keyword',
name='icon',
field=models.CharField(blank=True, max_length=16, null=True),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.0.4 on 2020-04-13 20:34
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0031_auto_20200407_1841'),
]
operations = [
migrations.AddField(
model_name='userpreference',
name='default_unit',
field=models.CharField(default='g', max_length=32),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 3.0.4 on 2020-04-13 20:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0032_userpreference_default_unit'),
]
operations = [
migrations.AddField(
model_name='userpreference',
name='default_page',
field=models.CharField(choices=[('SEARCH', 'Search'), ('PLAN', 'Meal-Plan')], default='SEARCH', max_length=64),
),
]

View File

@@ -41,9 +41,18 @@ class UserPreference(models.Model):
COLORS = ((PRIMARY, 'Primary'), (SECONDARY, 'Secondary'), (SUCCESS, 'Success'), (INFO, 'Info'), (WARNING, 'Warning'), (DANGER, 'Danger'), (LIGHT, 'Light'), (DARK, 'Dark'))
# Default Page
SEARCH = 'SEARCH'
PLAN = 'PLAN'
BOOKS = 'BOOKS'
PAGES = ((SEARCH, _('Search')), (PLAN, _('Meal-Plan')), (BOOKS, _('Books')), )
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
theme = models.CharField(choices=THEMES, max_length=128, default=FLATLY)
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
default_unit = models.CharField(max_length=32, default='g')
default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
def __str__(self):
return self.user
@@ -90,7 +99,7 @@ class SyncLog(models.Model):
class Keyword(models.Model):
name = models.CharField(max_length=64, unique=True)
icon = models.CharField(max_length=1, blank=True, null=True)
icon = models.CharField(max_length=16, blank=True, null=True)
description = models.TextField(default="", blank=True)
created_by = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)

View File

@@ -16,9 +16,10 @@ class Nextcloud(Provider):
@staticmethod
def get_client(storage):
options = {
'webdav_hostname': storage.url + '/remote.php/dav/files/' + storage.username,
'webdav_hostname': storage.url,
'webdav_login': storage.username,
'webdav_password': storage.password
'webdav_password': storage.password,
'webdav_root': '/remote.php/dav/files/' + storage.username
}
return wc.Client(options)

File diff suppressed because one or more lines are too long

View File

@@ -74,8 +74,8 @@
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item {% if request.resolver_match.url_name in 'index,edit_recipe,edit_internal_recipe,edit_external_recipe,view_recipe' %}active{% endif %}">
<a class="nav-link" href="{% url 'index' %}"><i class="fas fa-book"></i> {% trans 'Cookbook' %}<span
<li class="nav-item {% if request.resolver_match.url_name in 'view_search,edit_recipe,edit_internal_recipe,edit_external_recipe,view_recipe' %}active{% endif %}">
<a class="nav-link" href="{% url 'view_search' %}"><i class="fas fa-book"></i> {% trans 'Cookbook' %}<span
class="sr-only">(current)</span></a>
</li>

View File

@@ -26,6 +26,7 @@
</div>
{% if field.name == 'name' %}
<label>{% trans 'Ingredients' %}</label>
{{ form.ingredients.errors }}
<div id="ingredients-table"></div>
<br>
<div class="table-controls" style="text-align: center">
@@ -144,7 +145,7 @@
validator: "required",
editor: select2IngredientEditor
},
{title: "{% trans 'Amount' %}", field: "amount", validator: "required", editor: "input"},
{title: "{% trans 'Amount' %}", field: "amount", validator: "required", editor: "number"},
{
title: "{% trans 'Unit' %}",
field: "unit__name",
@@ -188,7 +189,7 @@
data.push({
ingredient__name: "{% trans 'Ingredient' %}",
amount: "100",
unit__name: "g",
unit__name: "{{ request.user.userpreference.default_unit }}",
note: "",
id: Math.floor(Math.random() * 10000000),
delete: false,

View File

@@ -8,6 +8,25 @@
{% endblock %}
{% block content %}
<style>
.mealplan-cell .mealplan-add-button{
text-align: center;
display: block;
}
@media (hover: hover) {
.mealplan-cell .mealplan-add-button{
visibility: hidden;
float: right;
display: inline;
}
.mealplan-cell:hover .mealplan-add-button{
visibility: initial;
}
}
</style>
<h3>
{% trans 'Meal-Plan' %} <a href="{% url 'new_meal_plan' %}"><i class="fas fa-plus-circle"></i></a>
@@ -53,7 +72,8 @@
</tr>
<tr>
{% for day_key, days_value in plan_value.days.items %}
<td>
<td class="mealplan-cell">
<a class="mealplan-add-button" href="{% url 'new_meal_plan' %}?date={{ day_key|date:'Y-m-d' }}&meal={{ plan_key }}"><i class="fas fa-plus"></i></a>
{% for mp in days_value %}
<a href="{% url 'edit_meal_plan' mp.pk %}"><i class="fas fa-edit"></i></a>
<a href="{% url 'view_recipe' mp.recipe.id %}">{{ mp.recipe.name }}</a><br/>

View File

@@ -4,7 +4,7 @@
{% load l10n %}
{% load custom_tags %}
{% block title %}{% trans 'View' %}{% endblock %}
{% block title %}{{ recipe.name }}{% endblock %}
{% block extra_head %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pretty-checkbox@3.0/dist/pretty-checkbox.min.css"
@@ -60,6 +60,8 @@
{% endif %}
<a class="btn btn-info" href="{% url 'new_meal_plan' %}?recipe={{ recipe.pk }}"><i
class="fas fa-calendar"></i></a>
<a class="btn btn-light" onclick="window.print()"><i
class="fas fa-print"></i></a>
</div>
</div>
@@ -98,7 +100,7 @@
<div class="row">
{% if ingredients %}
<div class="col-lg-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2">
<div class="col-md-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2">
<div class="card">
<div class="card-body">
<div class="row">
@@ -148,12 +150,16 @@
</td>
<td style="vertical-align: middle!important;">
{% if i.note %}
<button class="btn btn-light btn-sm" type="button" data-container="body"
<button class="btn btn-light btn-sm d-print-none" type="button"
data-container="body"
data-toggle="popover"
data-placement="right" data-html="true" data-trigger="focus"
data-content="{{ i.note }}">
<i class="fas fa-info"></i>
</button>
<div class="d-none d-print-block">
<i class="far fa-comment-alt"></i> {{ i.note }}
</div>
{% endif %}
</td>
</tr>
@@ -173,7 +179,7 @@
{% endif %}
{% if recipe.image %}
<div class="col-md-6 order-md-2 col-sm-12 order-sm-1 col-12 order-1 " style="text-align: center">
<div class="col-12 order-1 col-sm-12 order-sm-1 col-md-6 order-md-2" style="text-align: center">
<img class="img img-fluid rounded" src="{{ recipe.image.url }}" style="max-height: 30vh;"
alt="{% trans 'Recipe Image' %}">
<br/>
@@ -188,7 +194,7 @@
<div style="font-size: large">
{% if recipe.instructions %}
{{ recipe.instructions | markdown | safe }}
{{ recipe.instructions | markdown | safe | urlize }}
{% endif %}
</div>
@@ -294,7 +300,8 @@
<br/>
<br/>
<h5><i class="far fa-comments"></i> {% trans 'Comments' %}</h5>
<h5 {% if not comments %}class="d-print-none" {% endif %}><i class="far fa-comments"></i> {% trans 'Comments' %}
</h5>
{% for c in comments %}
<div class="card">
<div class="card-body">

View File

@@ -29,4 +29,4 @@ def delete_url(model, pk):
def markdown(value):
tags = markdown_tags + ['pre', 'table', 'td', 'tr', 'th', 'tbody', 'style', 'thead']
parsed_md = md.markdown(value, extensions=['markdown.extensions.fenced_code', 'tables', MarkdownFormatExtension()])
return bleach.clean(parsed_md, tags, print_attrs, markdown_attrs)
return bleach.clean(parsed_md, tags, markdown_attrs)

View File

@@ -7,10 +7,10 @@ class TestViewsGeneral(TestViews):
def test_index(self):
r = self.client.get(reverse('index'))
self.assertEqual(r.status_code, 200)
self.assertEqual(r.status_code, 302)
r = self.anonymous_client.get(reverse('index'))
self.assertEqual(r.status_code, 200)
self.assertEqual(r.status_code, 302)
def test_books(self):
url = reverse('view_books')

View File

@@ -8,6 +8,7 @@ from cookbook.helper import dal
urlpatterns = [
path('', views.index, name='index'),
path('search/', views.search, name='view_search'),
path('books/', views.books, name='view_books'),
path('plan/', views.meal_plan, name='view_plan'),
path('shopping/', views.shopping_list, name='view_shopping'),

View File

@@ -98,7 +98,10 @@ def internal_recipe_update(request, pk):
recipe_ingredient.ingredient = ingredient
if isinstance(i['amount'], str):
recipe_ingredient.amount = float(i['amount'].replace(',', '.'))
try:
recipe_ingredient.amount = float(i['amount'].replace(',', '.'))
except ValueError:
form.add_error("ingredients", _('There was an error converting your ingredients amount to a number: ') + i['unit__name'])
else:
recipe_ingredient.amount = i['amount']

View File

@@ -1,4 +1,5 @@
import re
from datetime import datetime
from django.contrib import messages
from django.contrib.auth.decorators import login_required
@@ -120,6 +121,12 @@ class MealPlanCreate(LoginRequiredMixin, CreateView):
form_class = MealPlanForm
success_url = reverse_lazy('view_plan')
def get_initial(self):
return dict(
meal=self.request.GET['meal'] if 'meal' in self.request.GET else None,
date=datetime.strptime(self.request.GET['date'], '%Y-%m-%d') if 'date' in self.request.GET else None
)
def form_valid(self, form):
obj = form.save(commit=False)
obj.user = self.request.user

View File

@@ -6,7 +6,9 @@ from django.contrib import messages
from django.contrib.auth import update_session_auth_hash
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import PasswordChangeForm
from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse_lazy
from django_tables2 import RequestConfig
from django.utils.translation import gettext as _
@@ -16,6 +18,21 @@ from cookbook.tables import RecipeTable
def index(request):
if not request.user.is_authenticated:
return HttpResponseRedirect(reverse_lazy('view_search'))
try:
page_map = {
UserPreference.SEARCH: reverse_lazy('view_search'),
UserPreference.PLAN: reverse_lazy('view_plan'),
UserPreference.BOOKS: reverse_lazy('view_books'),
}
return HttpResponseRedirect(page_map.get(request.user.userpreference.default_page))
except UserPreference.DoesNotExist:
return HttpResponseRedirect(reverse_lazy('view_search'))
def search(request):
if request.user.is_authenticated:
f = RecipeFilter(request.GET, queryset=Recipe.objects.all().order_by('name'))
@@ -137,7 +154,8 @@ def shopping_list(request):
if Recipe.objects.filter(pk=int(r)).exists():
recipes.append(int(r))
form = ShoppingForm(initial={'recipe': recipes})
markdown_format = False
form = ShoppingForm(initial={'recipe': recipes, 'markdown_format': False})
ingredients = []
@@ -174,6 +192,8 @@ def settings(request):
up = UserPreference(user=request.user)
up.theme = form.cleaned_data['theme']
up.nav_color = form.cleaned_data['nav_color']
up.default_unit = form.cleaned_data['default_unit']
up.default_page = form.cleaned_data['default_page']
up.save()
if 'user_name_form' in request.POST:

View File

@@ -0,0 +1,33 @@
kind: ConfigMap
apiVersion: v1
metadata:
labels:
app: recipes
name: recipes-nginx-config
data:
nginx-config: |-
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name _;
client_max_body_size 16M;
# serve static files
location /static/ {
alias /static/;
}
# serve media files
location /media/ {
alias /media/;
}
# pass requests for dynamic content to gunicorn
location / {
proxy_set_header Host $host;
proxy_pass http://localhost:8080;
}
}
}

50
docs/k8s/30-pv.yaml Normal file
View File

@@ -0,0 +1,50 @@
apiVersion: v1
kind: PersistentVolume
metadata:
name: recipes-db
labels:
app: recipes
type: local
tier: db
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
hostPath:
path: "/data/recipes/db"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: recipes-media
labels:
app: recipes
type: local
tier: media
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
hostPath:
path: "/data/recipes/media"
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: recipes-static
labels:
app: recipes
type: local
tier: static
spec:
storageClassName: manual
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
hostPath:
path: "/data/recipes/static"

52
docs/k8s/30-pvc.yaml Normal file
View File

@@ -0,0 +1,52 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: recipes-db
labels:
app: recipes
spec:
selector:
matchLabels:
tier: db
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: recipes-media
labels:
app: recipes
spec:
selector:
matchLabels:
tier: media
app: recipes
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: recipes-static
labels:
app: recipes
spec:
selector:
matchLabels:
tier: static
app: recipes
storageClassName: manual
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi

102
docs/k8s/50-deployment.yaml Normal file
View File

@@ -0,0 +1,102 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: recipes
labels:
app: recipes
environment: production
tier: frontend
spec:
replicas: 1
strategy:
type: RollingUpdate
selector:
matchLabels:
app: recipes
environment: production
template:
metadata:
labels:
app: recipes
environment: production
spec:
containers:
- name: recipes-nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
protocol: TCP
name: http
volumeMounts:
- mountPath: '/media'
name: media
- mountPath: '/static'
name: static
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx-config
readOnly: true
- name: recipes
image: 'vabene1111/recipes:latest'
imagePullPolicy: IfNotPresent
livenessProbe:
httpGet:
path: /
port: 8080
readinessProbe:
httpGet:
path: /
port: 8080
volumeMounts:
- mountPath: '/opt/recipes/mediafiles'
name: media
- mountPath: '/opt/recipes/staticfiles'
name: static
env:
- name: DEBUG
value: "0"
- name: ALLOWED_HOSTS
value: '*'
- name: SECRET_KEY
value: # CHANGEME
- name: DB_ENGINE
value: django.db.backends.postgresql_psycopg2
- name: POSTGRES_HOST
value: localhost
- name: POSTGRES_PORT
value: "5432"
- name: POSTGRES_USER
value: recipes
- name: POSTGRES_DB
value: recipes
- name: POSTGRES_PASSWORD
value: # CHANGEME
- name: recipes-db
image: 'postgres:latest'
imagePullPolicy: IfNotPresent
ports:
- containerPort: 5432
volumeMounts:
- mountPath: '/var/lib/postgresql/data'
name: database
env:
- name: POSTGRES_USER
value: recipes
- name: POSTGRES_DB
value: recipes
- name: POSTGRES_PASSWORD
value: # CHANGEME
volumes:
- name: database
persistentVolumeClaim:
claimName: recipes-db
- name: media
persistentVolumeClaim:
claimName: recipes-media
- name: static
persistentVolumeClaim:
claimName: recipes-static
- name: nginx-config
configMap:
name: recipes-nginx-config

15
docs/k8s/60-service.yaml Normal file
View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: recipes
labels:
app: recipes
spec:
selector:
app: recipes
environment: production
ports:
- port: 80
targetPort: http
name: http
protocol: TCP

25
docs/k8s/README.md Normal file
View File

@@ -0,0 +1,25 @@
# Kubernetes
This is a basic kubernetes setup. Please note that this does not necessarily follow Kubernetes best practices and should only used as a basis to build your own setup from!
## Important notes
State (database, static files and media files) is handled via `PersistentVolumes`.
Note that you will most likely have to change the `PersistentVolumes` in `30-pv.yaml`. The current setup is only usable for a single-node cluster because it uses local storage on the kubernetes worker nodes under `/data/recipes/`. It should just serve as an example.
Currently, the deployment in `50-deployment.yaml` just pulls the `latest` tag of all containers. In a production setup, you should set this to a fixed version!
See env variables tagged with `CHANGEME` in `50-deployment.yaml` and make sure to change those! A better setup would use kubernetes secrets but this is not implemented yet.
## Updates
These manifests are not tested against new versions.
## Apply the manifets
To apply the manifest with `kubectl`, use the following command:
```
kubectl apply -f ./docs/k8s/
```