diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f26e7ee..b73d37d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -78,6 +78,7 @@ deploy:staging: api ./deploy \ --values deploy/values.staging.yaml \ --set image.tag=$TAG \ + --set migrations.image.tag=$TAG \ --set imageCredentials.username=$REGISTRY_USERNAME \ --set imageCredentials.password=$REGISTRY_PASSWORD \ --set config.database.url=$DATABASE_URL diff --git a/Makefile b/Makefile index 9335fc2..3672618 100644 --- a/Makefile +++ b/Makefile @@ -49,3 +49,8 @@ build-docker-httpserver: .PHONY: build-docker-migrations build-docker-migrations: docker build -t yggdrasil-migrations -f ./build/package/migrations/Dockerfile . + +.PHONY: helm-test +helm-test: + helm lint ./deploy + helm template api ./deploy --values ./deploy/values.test.yaml diff --git a/api/v1/openapi.yaml b/api/v1/openapi.yaml index 4ebef8d..e9e85fd 100644 --- a/api/v1/openapi.yaml +++ b/api/v1/openapi.yaml @@ -560,6 +560,9 @@ components: $ref: "#/components/schemas/TrainingPrice" slug: type: string + published: + type: boolean + default: false required: - name - days @@ -573,9 +576,13 @@ components: properties: id: $ref: "#/components/schemas/TrainingID" + retired: + type: boolean required: - id - slug + - published + - retired TrainingID: type: integer diff --git a/db/migrations/002_training-published-and-retired.down.sql b/db/migrations/002_training-published-and-retired.down.sql new file mode 100644 index 0000000..9f629b1 --- /dev/null +++ b/db/migrations/002_training-published-and-retired.down.sql @@ -0,0 +1,3 @@ +ALTER TABLE training.trainings + DROP COLUMN published, + DROP COLUMN retired; diff --git a/db/migrations/002_training-published-and-retired.up.sql b/db/migrations/002_training-published-and-retired.up.sql new file mode 100644 index 0000000..efb08ed --- /dev/null +++ b/db/migrations/002_training-published-and-retired.up.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE training.trainings ADD COLUMN published BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE training.trainings ADD COLUMN retired BOOLEAN NOT NULL DEFAULT FALSE; + +COMMIT; diff --git a/deploy/templates/deployment.yaml b/deploy/templates/deployment.yaml index b5b5236..6ba38f9 100644 --- a/deploy/templates/deployment.yaml +++ b/deploy/templates/deployment.yaml @@ -34,7 +34,7 @@ spec: {{- toYaml . | nindent 8 }} {{- end }} containers: - - name: {{ .Chart.Name }} + - name: httpserver image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" imagePullPolicy: Always ports: diff --git a/deploy/templates/migration-job.yaml b/deploy/templates/migration-job.yaml new file mode 100644 index 0000000..8ea39ba --- /dev/null +++ b/deploy/templates/migration-job.yaml @@ -0,0 +1,46 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-migrations-{{ now | unixEpoch }} + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: "database-migrations" + batch.kubernetes.io/job-name: {{ .Release.Name }}-migrations-{{ now | unixEpoch }} +spec: + ttlSecondsAfterFinished: 604800 # 1 week in seconds + template: + metadata: + labels: + app.kubernetes.io/name: {{ .Chart.Name }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: "database-migrations" + batch.kubernetes.io/job-name: {{ .Release.Name }}-migrations-{{ now | unixEpoch }} + spec: + automountServiceAccountToken: false + {{- with .Values.migrations.image.pullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: migrations + image: "{{ .Values.migrations.image.repository }}:{{ .Values.migrations.image.tag }}" + command: ["/bin/ash"] + args: + - -c + - | + migrate -path /srv/migrations -database $(cat /etc/yggdrasil/secrets/database_url.txt) up + {{- with .Values.migrations.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - mountPath: /etc/yggdrasil/secrets + name: secrets + readOnly: true + volumes: + - name: secrets + secret: + secretName: {{ .Release.Name }} + restartPolicy: Never + backoffLimit: 0 diff --git a/deploy/templates/service.yaml b/deploy/templates/service.yaml index 83f2df6..df60c7b 100644 --- a/deploy/templates/service.yaml +++ b/deploy/templates/service.yaml @@ -6,7 +6,7 @@ spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port.number }} - targetPort: {{ .Values.service.port.number }} + targetPort: {{ .Values.service.port.name }} protocol: TCP name: {{ .Values.service.port.name }} selector: diff --git a/deploy/values.staging.yaml b/deploy/values.staging.yaml index 2fa7bba..87d2517 100644 --- a/deploy/values.staging.yaml +++ b/deploy/values.staging.yaml @@ -3,9 +3,17 @@ ingress: host: staging.yggdrasil.vmdevel.cz annotations: cert-manager.io/cluster-issuer: letsencrypt-dns-production + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-origin: "https://staging.midgard.vmdevel.cz,http://localhost:5173" + nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE" replicas: 2 image: pullSecrets: - name: mareshq-gitlab-registry + +migrations: + image: + pullSecrets: + - name: mareshq-gitlab-registry diff --git a/deploy/values.test.yaml b/deploy/values.test.yaml index b88b000..1b5e060 100644 --- a/deploy/values.test.yaml +++ b/deploy/values.test.yaml @@ -2,8 +2,7 @@ ingress: enabled: true host: yggdrasil.example.com annotations: - cert-manager.io/cluster-issuer: letsencrypt-prod - cert-manager.io/issuer: letsencrypt-prod + cert-manager.io/cluster-issuer: letsencrypt-staging replicas: 2 diff --git a/deploy/values.yaml b/deploy/values.yaml index d445df3..f5afb52 100644 --- a/deploy/values.yaml +++ b/deploy/values.yaml @@ -31,3 +31,17 @@ config: port: 8080 database: url: + +migrations: + image: + pullSecrets: [] + repository: registry.mareshq.com/hq/yggdrasil/migrations + tag: latest + + resources: + limits: + cpu: 250m + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi diff --git a/internal/faker/faker.go b/internal/faker/faker.go index adf7380..00f2e53 100644 --- a/internal/faker/faker.go +++ b/internal/faker/faker.go @@ -23,10 +23,31 @@ func NewFaker(trainingRepository training.Repository, trainingDateRepository tra func (f *Faker) GenerateFakeData() error { var trainings = []training.Training{ + { + Name: "Kubernetes v1", + Days: 1, + Description: "Kubernetes-v1", + Published: false, + Retired: true, + Pricing: []training.Price{ + { + Amount: decimal.NewFromInt(3900), + Currency: money.CZK, + Type: training.OpenTrainingPrice, + }, + { + Amount: decimal.NewFromInt(24000), + Currency: money.CZK, + Type: training.CorporateTrainingPrice, + }, + }, + }, { Name: "Kubernetes", Days: 2, Description: "Kubernetes", + Published: true, + Retired: false, Pricing: []training.Price{ { Amount: decimal.NewFromInt(450), @@ -54,6 +75,8 @@ func (f *Faker) GenerateFakeData() error { Name: "Terraform", Days: 1, Description: "Terraform", + Published: true, + Retired: false, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), @@ -81,6 +104,8 @@ func (f *Faker) GenerateFakeData() error { Name: "RKE2", Days: 1, Description: "RKE2", + Published: false, + Retired: false, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), @@ -108,6 +133,8 @@ func (f *Faker) GenerateFakeData() error { Name: "GitHub Actions", Days: 1, Description: "GitHub Actions", + Published: false, + Retired: false, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), @@ -135,6 +162,8 @@ func (f *Faker) GenerateFakeData() error { Name: "GitLab CI", Days: 1, Description: "GitLab CI", + Published: true, + Retired: false, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), @@ -162,6 +191,8 @@ func (f *Faker) GenerateFakeData() error { Name: "Prometheus", Days: 2, Description: "Prometheus", + Published: true, + Retired: false, Pricing: []training.Price{ { Amount: decimal.NewFromInt(450), diff --git a/internal/server/api.gen.go b/internal/server/api.gen.go index 0d363d9..acce8ef 100644 --- a/internal/server/api.gen.go +++ b/internal/server/api.gen.go @@ -1,6 +1,6 @@ // Package server provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/deepmap/oapi-codegen/v2 version v2.2.0 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.3.0 DO NOT EDIT. package server import ( @@ -83,6 +83,7 @@ type NewTraining struct { Description string `json:"description"` Name string `json:"name"` Pricing []TrainingPrice `json:"pricing"` + Published *bool `json:"published,omitempty"` Slug *string `json:"slug,omitempty"` } @@ -139,6 +140,8 @@ type Training struct { Id TrainingID `json:"id"` Name string `json:"name"` Pricing []TrainingPrice `json:"pricing"` + Published bool `json:"published"` + Retired bool `json:"retired"` Slug string `json:"slug"` } @@ -2431,45 +2434,46 @@ func (sh *strictHandler) ListTrainingUpcomingDates(ctx *fiber.Ctx, trainingID Tr // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", + "rUcw74lf4Jvfz38LAAD//+HPwchvWQAA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/internal/server/api.go b/internal/server/api.go index 5f8f23a..d375170 100644 --- a/internal/server/api.go +++ b/internal/server/api.go @@ -85,11 +85,18 @@ func (h *APIHandlers) CreateTraining(ctx context.Context, req CreateTrainingRequ slug = *req.Body.Slug } + published := false + if req.Body.Published != nil { + published = *req.Body.Published + } + t := training.Training{ Name: req.Body.Name, Slug: slug, Days: req.Body.Days, Description: req.Body.Description, + Published: published, + Retired: false, Pricing: pricing, } @@ -118,6 +125,8 @@ func (h *APIHandlers) CreateTraining(ctx context.Context, req CreateTrainingRequ Slug: t.Slug, Days: t.Days, Description: t.Description, + Published: t.Published, + Retired: t.Retired, Pricing: responsePricing, }, nil } @@ -173,6 +182,8 @@ func (h *APIHandlers) GetTraining(ctx context.Context, req GetTrainingRequestObj Slug: t.Slug, Days: t.Days, Description: t.Description, + Published: t.Published, + Retired: t.Retired, Pricing: pricing, }, nil } @@ -208,6 +219,8 @@ func (h *APIHandlers) GetTrainingBySlug(ctx context.Context, req GetTrainingBySl Slug: t.Slug, Days: t.Days, Description: t.Description, + Published: t.Published, + Retired: t.Retired, Pricing: pricing, }, nil } @@ -237,12 +250,19 @@ func (h *APIHandlers) UpdateTraining(ctx context.Context, req UpdateTrainingRequ slug = *req.Body.Slug } + published := false + if req.Body.Published != nil { + published = *req.Body.Published + } + t := training.Training{ ID: req.TrainingID, Name: req.Body.Name, Slug: slug, Days: req.Body.Days, Description: req.Body.Description, + Published: published, + Retired: false, Pricing: pricing, } @@ -262,6 +282,8 @@ func (h *APIHandlers) UpdateTraining(ctx context.Context, req UpdateTrainingRequ Slug: t.Slug, Days: t.Days, Description: t.Description, + Published: t.Published, + Retired: t.Retired, }, nil } diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 73e0634..e7fd38b 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -172,6 +172,8 @@ func TestServer(t *testing.T) { assert.Equal(t, tr.Name, trr.Name) assert.Equal(t, tr.ID, trr.Id) assert.Equal(t, tr.Slug, trr.Slug) + assert.Equal(t, false, trr.Published) + assert.Equal(t, false, trr.Retired) }) t.Run("List all trainings", func(t *testing.T) { @@ -202,6 +204,8 @@ func TestServer(t *testing.T) { assert.NotEmpty(t, trr.Slug) assert.NotEmpty(t, trr.Description) assert.NotEmpty(t, trr.Days) + assert.False(t, trr.Published) + assert.False(t, trr.Retired) } }) @@ -246,6 +250,8 @@ func TestServer(t *testing.T) { assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, updTr.Name, trr.Name) assert.Equal(t, updatedSlug, trr.Slug) + assert.Equal(t, false, trr.Published) + assert.Equal(t, false, trr.Retired) }) t.Run("Delete training", func(t *testing.T) { diff --git a/pkg/training/model.go b/pkg/training/model.go index adaa5ea..cd1c548 100644 --- a/pkg/training/model.go +++ b/pkg/training/model.go @@ -14,6 +14,8 @@ type Training struct { Slug string Days int8 Description string + Published bool + Retired bool Pricing []Price `db:"-"` } diff --git a/pkg/training/postgres_repository.go b/pkg/training/postgres_repository.go index 5300d4b..233bd10 100644 --- a/pkg/training/postgres_repository.go +++ b/pkg/training/postgres_repository.go @@ -30,10 +30,10 @@ func (r *PostgresTrainingRepository) Create(training *Training) error { } queryErr := tx.QueryRow(ctx, ` - INSERT INTO training.trainings (name, slug, description, days) - VALUES ($1, $2, $3, $4) + INSERT INTO training.trainings (name, slug, description, days, published) + VALUES ($1, $2, $3, $4, $5) RETURNING id - `, training.Name, training.Slug, training.Description, training.Days).Scan(&training.ID) + `, training.Name, training.Slug, training.Description, training.Days, training.Published).Scan(&training.ID) if queryErr != nil { return queryErr } @@ -77,10 +77,10 @@ func (r *PostgresTrainingRepository) FindByID(id ID) (*Training, error) { var training Training err := r.pg.QueryRow(ctx, ` - SELECT id, name, slug, description, days + SELECT id, name, slug, description, days, published, retired FROM training.trainings WHERE id = $1 - `, id).Scan(&training.ID, &training.Name, &training.Slug, &training.Description, &training.Days) + `, id).Scan(&training.ID, &training.Name, &training.Slug, &training.Description, &training.Days, &training.Published, &training.Retired) if err != nil { return nil, err @@ -122,10 +122,10 @@ func (r *PostgresTrainingRepository) FindBySlug(slug string) (*Training, error) var training Training err := r.pg.QueryRow(ctx, ` - SELECT id, name, slug, description, days + SELECT id, name, slug, description, days, published, retired FROM training.trainings WHERE slug = $1 - `, slug).Scan(&training.ID, &training.Name, &training.Slug, &training.Description, &training.Days) + `, slug).Scan(&training.ID, &training.Name, &training.Slug, &training.Description, &training.Days, &training.Published, &training.Retired) if err != nil { return nil, err @@ -172,7 +172,7 @@ func (r *PostgresTrainingRepository) FindAll() ([]Training, error) { trainings, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (Training, error) { var training Training - scanErr := row.Scan(&training.ID, &training.Name, &training.Slug, &training.Description, &training.Days) + scanErr := row.Scan(&training.ID, &training.Name, &training.Slug, &training.Description, &training.Days, &training.Published, &training.Retired) if scanErr != nil { return Training{}, scanErr } @@ -217,9 +217,9 @@ func (r *PostgresTrainingRepository) Update(training *Training) error { _, err = tx.Exec(ctx, ` UPDATE training.trainings - SET name = $1, slug = $2, description = $3, days = $4 + SET name = $1, slug = $2, description = $3, days = $4, published = $5, retired = $6 WHERE id = $4 - `, training.Name, training.Slug, training.Description, training.Days, training.ID) + `, training.Name, training.Slug, training.Description, training.Days, training.ID, training.Published, training.Retired) if err != nil { return err