diff --git a/momentum-core/config/config.go b/momentum-core/config/config.go index e609b77..63fd4c7 100644 --- a/momentum-core/config/config.go +++ b/momentum-core/config/config.go @@ -81,6 +81,14 @@ func checkMandatoryTemplates(config *MomentumConfig) error { errs := make([]error, 0) + templatePath := TemplateDir(config) + if !utils.FileExists(templatePath) { + err := utils.DirCreate(templatePath) + if err != nil { + errs = append(errs, err) + } + } + appTemplatePath := ApplicationTemplatesPath(config) if !utils.FileExists(appTemplatePath) { err := utils.DirCreate(appTemplatePath) @@ -127,6 +135,10 @@ func initializeRepository(config *MomentumConfig) error { cloneRepoTo(repoUrl, "", "", config.RepoDir()) + if !utils.FileExists(filepath.Join(config.RepoDir(), MOMENTUM_ROOT)) { + return errors.New("invalid momentum repository") + } + return nil } diff --git a/momentum-core/config/routes.go b/momentum-core/config/endpoints.go similarity index 76% rename from momentum-core/config/routes.go rename to momentum-core/config/endpoints.go index 42d71a8..e2ba0f7 100644 --- a/momentum-core/config/routes.go +++ b/momentum-core/config/endpoints.go @@ -17,3 +17,7 @@ const API_TEMPLATES_PREFIX = API_VERSION_BETA + "/templates" const API_TEMPLATES_APPLICATIONS = API_TEMPLATES_PREFIX + "/applications" const API_TEMPLATES_STAGES = API_TEMPLATES_PREFIX + "/stages" const API_TEMPLATES_DEPLOYMENTS = API_TEMPLATES_PREFIX + "/deployments" +const API_TEMPLATES_ADD = API_TEMPLATES_PREFIX +const API_TEMPLATES_SPEC_PREFIX = API_TEMPLATES_PREFIX + "/spec" +const API_TEMPLATE_GET_SPEC = API_TEMPLATES_SPEC_PREFIX + "/:templateName" +const API_TEMPLATE_APPLY = API_TEMPLATES_SPEC_PREFIX + "/apply/:anchorArtefactId" diff --git a/momentum-core/docs/docs.go b/momentum-core/docs/docs.go index 8760576..6402ee0 100644 --- a/momentum-core/docs/docs.go +++ b/momentum-core/docs/docs.go @@ -163,6 +163,17 @@ const docTemplate = `{ "files" ], "summary": "adds a new file to a given parent (triggers transaction)", + "parameters": [ + { + "description": "the body shall contain a File instance", + "name": "CreateFileRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/files.CreateFileRequest" + } + } + ], "responses": { "200": { "description": "OK", @@ -247,6 +258,24 @@ const docTemplate = `{ "files" ], "summary": "updates the given file (triggers transaction)", + "parameters": [ + { + "type": "string", + "description": "file id", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "the body shall contain a File instance", + "name": "File", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/files.File" + } + } + ], "responses": { "200": { "description": "OK", @@ -306,7 +335,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/files.Overwrite" + "$ref": "#/definitions/overwrites.Overwrite" } } }, @@ -377,6 +406,57 @@ const docTemplate = `{ } } }, + "/api/beta/templates": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "templates" + ], + "summary": "adds a new template (triggers transaction)", + "parameters": [ + { + "description": "the body shall contain a CreateTemplateRequest instance", + "name": "CreateTemplateRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/templates.CreateTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/templates.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + } + } + } + }, "/api/beta/templates/applications": { "get": { "produces": [ @@ -392,7 +472,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/templates.Template" + "type": "string" } } }, @@ -432,7 +512,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/templates.Template" + "type": "string" } } }, @@ -457,6 +537,110 @@ const docTemplate = `{ } } }, + "/api/beta/templates/spec/:templateName": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "templates" + ], + "summary": "gets the spec for a template, which contains values to be set when applying the template", + "parameters": [ + { + "type": "string", + "description": "name of the template (template names are unique)", + "name": "templateName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/templates.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + } + } + } + }, + "/api/beta/templates/spec/apply/:anchorArtefactId": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "templates" + ], + "summary": "gets the spec for a template, which contains values to be set when applying the template (triggers transaction)", + "parameters": [ + { + "type": "string", + "description": "id of the artefact where the template shall be applied. Must be a directory.", + "name": "anchorArtefactId", + "in": "path", + "required": true + }, + { + "description": "the body shall contain a CreateTemplateRequest instance", + "name": "TemplateSpec", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/templates.TemplateSpec" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/templates.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + } + } + } + }, "/api/beta/templates/stages": { "get": { "produces": [ @@ -472,7 +656,7 @@ const docTemplate = `{ "schema": { "type": "array", "items": { - "$ref": "#/definitions/templates.Template" + "type": "string" } } }, @@ -564,6 +748,20 @@ const docTemplate = `{ } } }, + "files.CreateFileRequest": { + "type": "object", + "properties": { + "body": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "string" + } + } + }, "files.Dir": { "type": "object", "properties": { @@ -601,7 +799,7 @@ const docTemplate = `{ } } }, - "files.Overwrite": { + "overwrites.Overwrite": { "type": "object", "properties": { "originFileId": { @@ -618,17 +816,93 @@ const docTemplate = `{ } } }, - "templates.Template": { + "templates.CreateTemplateRequest": { "type": "object", "properties": { "template": { - "$ref": "#/definitions/files.Dir" + "description": "the toplevel directories name is the name of the template", + "allOf": [ + { + "$ref": "#/definitions/templates.TemplateDir" + } + ] + }, + "templateConfig": { + "$ref": "#/definitions/templates.TemplateConfig" }, "templateKind": { "$ref": "#/definitions/templates.TemplateKind" } } }, + "templates.Template": { + "type": "object", + "properties": { + "children": { + "description": "The children are templates which are contained within the template.", + "type": "array", + "items": { + "$ref": "#/definitions/templates.Template" + } + }, + "kind": { + "$ref": "#/definitions/templates.TemplateKind" + }, + "name": { + "description": "be aware, that each template must have an unique name\nit doesn't matter if they are of different template kind", + "type": "string" + }, + "root": { + "$ref": "#/definitions/files.Dir" + } + } + }, + "templates.TemplateConfig": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "kind": { + "$ref": "#/definitions/templates.TemplateKind" + } + } + }, + "templates.TemplateDir": { + "type": "object", + "properties": { + "directories": { + "type": "array", + "items": { + "$ref": "#/definitions/templates.TemplateDir" + } + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/templates.TemplateFile" + } + }, + "name": { + "type": "string" + } + } + }, + "templates.TemplateFile": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "templateBody": { + "description": "base64 encoded", + "type": "string" + } + } + }, "templates.TemplateKind": { "type": "integer", "enum": [ @@ -641,6 +915,37 @@ const docTemplate = `{ "STAGE", "DEPLOYMENT" ] + }, + "templates.TemplateSpec": { + "type": "object", + "properties": { + "template": { + "$ref": "#/definitions/templates.Template" + }, + "valueSpecs": { + "type": "array", + "items": { + "$ref": "#/definitions/templates.ValueSpec" + } + } + } + }, + "templates.ValueSpec": { + "type": "object", + "properties": { + "name": { + "description": "name of the value (name displayed in frontend)", + "type": "string" + }, + "templateName": { + "description": "name of the template which the value belongs to", + "type": "string" + }, + "value": { + "description": "the value assigned", + "type": "string" + } + } } } }` diff --git a/momentum-core/docs/swagger.json b/momentum-core/docs/swagger.json index cf67ca4..d05e863 100644 --- a/momentum-core/docs/swagger.json +++ b/momentum-core/docs/swagger.json @@ -160,6 +160,17 @@ "files" ], "summary": "adds a new file to a given parent (triggers transaction)", + "parameters": [ + { + "description": "the body shall contain a File instance", + "name": "CreateFileRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/files.CreateFileRequest" + } + } + ], "responses": { "200": { "description": "OK", @@ -244,6 +255,24 @@ "files" ], "summary": "updates the given file (triggers transaction)", + "parameters": [ + { + "type": "string", + "description": "file id", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "the body shall contain a File instance", + "name": "File", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/files.File" + } + } + ], "responses": { "200": { "description": "OK", @@ -303,7 +332,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/files.Overwrite" + "$ref": "#/definitions/overwrites.Overwrite" } } }, @@ -374,6 +403,57 @@ } } }, + "/api/beta/templates": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "templates" + ], + "summary": "adds a new template (triggers transaction)", + "parameters": [ + { + "description": "the body shall contain a CreateTemplateRequest instance", + "name": "CreateTemplateRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/templates.CreateTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/templates.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + } + } + } + }, "/api/beta/templates/applications": { "get": { "produces": [ @@ -389,7 +469,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/templates.Template" + "type": "string" } } }, @@ -429,7 +509,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/templates.Template" + "type": "string" } } }, @@ -454,6 +534,110 @@ } } }, + "/api/beta/templates/spec/:templateName": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "templates" + ], + "summary": "gets the spec for a template, which contains values to be set when applying the template", + "parameters": [ + { + "type": "string", + "description": "name of the template (template names are unique)", + "name": "templateName", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/templates.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + } + } + } + }, + "/api/beta/templates/spec/apply/:anchorArtefactId": { + "post": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "templates" + ], + "summary": "gets the spec for a template, which contains values to be set when applying the template (triggers transaction)", + "parameters": [ + { + "type": "string", + "description": "id of the artefact where the template shall be applied. Must be a directory.", + "name": "anchorArtefactId", + "in": "path", + "required": true + }, + { + "description": "the body shall contain a CreateTemplateRequest instance", + "name": "TemplateSpec", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/templates.TemplateSpec" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/templates.Template" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/config.ApiError" + } + } + } + } + }, "/api/beta/templates/stages": { "get": { "produces": [ @@ -469,7 +653,7 @@ "schema": { "type": "array", "items": { - "$ref": "#/definitions/templates.Template" + "type": "string" } } }, @@ -561,6 +745,20 @@ } } }, + "files.CreateFileRequest": { + "type": "object", + "properties": { + "body": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parentId": { + "type": "string" + } + } + }, "files.Dir": { "type": "object", "properties": { @@ -598,7 +796,7 @@ } } }, - "files.Overwrite": { + "overwrites.Overwrite": { "type": "object", "properties": { "originFileId": { @@ -615,17 +813,93 @@ } } }, - "templates.Template": { + "templates.CreateTemplateRequest": { "type": "object", "properties": { "template": { - "$ref": "#/definitions/files.Dir" + "description": "the toplevel directories name is the name of the template", + "allOf": [ + { + "$ref": "#/definitions/templates.TemplateDir" + } + ] + }, + "templateConfig": { + "$ref": "#/definitions/templates.TemplateConfig" }, "templateKind": { "$ref": "#/definitions/templates.TemplateKind" } } }, + "templates.Template": { + "type": "object", + "properties": { + "children": { + "description": "The children are templates which are contained within the template.", + "type": "array", + "items": { + "$ref": "#/definitions/templates.Template" + } + }, + "kind": { + "$ref": "#/definitions/templates.TemplateKind" + }, + "name": { + "description": "be aware, that each template must have an unique name\nit doesn't matter if they are of different template kind", + "type": "string" + }, + "root": { + "$ref": "#/definitions/files.Dir" + } + } + }, + "templates.TemplateConfig": { + "type": "object", + "properties": { + "children": { + "type": "array", + "items": { + "type": "string" + } + }, + "kind": { + "$ref": "#/definitions/templates.TemplateKind" + } + } + }, + "templates.TemplateDir": { + "type": "object", + "properties": { + "directories": { + "type": "array", + "items": { + "$ref": "#/definitions/templates.TemplateDir" + } + }, + "files": { + "type": "array", + "items": { + "$ref": "#/definitions/templates.TemplateFile" + } + }, + "name": { + "type": "string" + } + } + }, + "templates.TemplateFile": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "templateBody": { + "description": "base64 encoded", + "type": "string" + } + } + }, "templates.TemplateKind": { "type": "integer", "enum": [ @@ -638,6 +912,37 @@ "STAGE", "DEPLOYMENT" ] + }, + "templates.TemplateSpec": { + "type": "object", + "properties": { + "template": { + "$ref": "#/definitions/templates.Template" + }, + "valueSpecs": { + "type": "array", + "items": { + "$ref": "#/definitions/templates.ValueSpec" + } + } + } + }, + "templates.ValueSpec": { + "type": "object", + "properties": { + "name": { + "description": "name of the value (name displayed in frontend)", + "type": "string" + }, + "templateName": { + "description": "name of the template which the value belongs to", + "type": "string" + }, + "value": { + "description": "the value assigned", + "type": "string" + } + } } } } \ No newline at end of file diff --git a/momentum-core/docs/swagger.yaml b/momentum-core/docs/swagger.yaml index 2f617e8..9e28c5b 100644 --- a/momentum-core/docs/swagger.yaml +++ b/momentum-core/docs/swagger.yaml @@ -1,427 +1,631 @@ - basePath: / - definitions: - artefacts.Artefact: - properties: - contentIds: - description: id's of children artefacts - items: - type: string - type: array - id: +basePath: / +definitions: + artefacts.Artefact: + properties: + contentIds: + description: id's of children artefacts + items: type: string - name: - type: string - parentId: - description: id of parent artefacts - type: string - type: - $ref: '#/definitions/artefacts.ArtefactType' - type: object - artefacts.ArtefactType: - enum: - - 1 - - 2 - - 4 - - 8 - - 16 - - 32 - type: integer - x-enum-varnames: - - ROOT - - META - - APPLICATION - - STAGE - - DEPLOYMENT - - FILE - config.ApiError: - properties: - detail: - type: string - error: {} - instance: - type: string - status: - type: integer - title: - type: string - type: - type: string - type: object - files.Dir: - properties: - files: - items: - $ref: '#/definitions/files.File' - type: array - id: - type: string - name: - type: string - subDirs: - items: - $ref: '#/definitions/files.Dir' - type: array - type: object - files.File: - properties: - body: - type: string - id: - type: string - name: - type: string - type: object - files.Overwrite: - properties: - originFileId: - type: string - originFileLine: - type: integer - overwriteFileId: - type: string - overwriteFileLine: - type: integer - type: object - templates.Template: - properties: - template: + type: array + id: + type: string + name: + type: string + parentId: + description: id of parent artefacts + type: string + type: + $ref: '#/definitions/artefacts.ArtefactType' + type: object + artefacts.ArtefactType: + enum: + - 1 + - 2 + - 4 + - 8 + - 16 + - 32 + type: integer + x-enum-varnames: + - ROOT + - META + - APPLICATION + - STAGE + - DEPLOYMENT + - FILE + config.ApiError: + properties: + detail: + type: string + error: {} + instance: + type: string + status: + type: integer + title: + type: string + type: + type: string + type: object + files.CreateFileRequest: + properties: + body: + type: string + name: + type: string + parentId: + type: string + type: object + files.Dir: + properties: + files: + items: + $ref: '#/definitions/files.File' + type: array + id: + type: string + name: + type: string + subDirs: + items: $ref: '#/definitions/files.Dir' - templateKind: - $ref: '#/definitions/templates.TemplateKind' - type: object - templates.TemplateKind: - enum: - - 1 - - 2 - - 4 - type: integer - x-enum-varnames: - - APPLICATION - - STAGE - - DEPLOYMENT - host: localhost:8080 - info: - contact: {} - description: The momentum core api manages the core structure of momentum - license: - name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html - title: Momentum Core API - version: early-alpha - paths: - /api/beta/applications: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/artefacts.Artefact' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets a list of all applications - tags: - - artefacts - /api/beta/artefact/{id}/: - get: - parameters: - - description: artefact id - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/artefacts.Artefact' - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: get an artefact by id (an artefact can be any of APPLICATION, STAGE - or DEPLOYMENT) - tags: - - artefacts - /api/beta/deployments: - get: - parameters: - - description: stage id - in: query - name: stageId - required: true + type: array + type: object + files.File: + properties: + body: + type: string + id: + type: string + name: + type: string + type: object + overwrites.Overwrite: + properties: + originFileId: + type: string + originFileLine: + type: integer + overwriteFileId: + type: string + overwriteFileLine: + type: integer + type: object + templates.CreateTemplateRequest: + properties: + template: + allOf: + - $ref: '#/definitions/templates.TemplateDir' + description: the toplevel directories name is the name of the template + templateConfig: + $ref: '#/definitions/templates.TemplateConfig' + templateKind: + $ref: '#/definitions/templates.TemplateKind' + type: object + templates.Template: + properties: + children: + description: The children are templates which are contained within the template. + items: + $ref: '#/definitions/templates.Template' + type: array + kind: + $ref: '#/definitions/templates.TemplateKind' + name: + description: |- + be aware, that each template must have an unique name + it doesn't matter if they are of different template kind + type: string + root: + $ref: '#/definitions/files.Dir' + type: object + templates.TemplateConfig: + properties: + children: + items: type: string - produces: - - application/json - responses: - "200": - description: OK - schema: + type: array + kind: + $ref: '#/definitions/templates.TemplateKind' + type: object + templates.TemplateDir: + properties: + directories: + items: + $ref: '#/definitions/templates.TemplateDir' + type: array + files: + items: + $ref: '#/definitions/templates.TemplateFile' + type: array + name: + type: string + type: object + templates.TemplateFile: + properties: + name: + type: string + templateBody: + description: base64 encoded + type: string + type: object + templates.TemplateKind: + enum: + - 1 + - 2 + - 4 + type: integer + x-enum-varnames: + - APPLICATION + - STAGE + - DEPLOYMENT + templates.TemplateSpec: + properties: + template: + $ref: '#/definitions/templates.Template' + valueSpecs: + items: + $ref: '#/definitions/templates.ValueSpec' + type: array + type: object + templates.ValueSpec: + properties: + name: + description: name of the value (name displayed in frontend) + type: string + templateName: + description: name of the template which the value belongs to + type: string + value: + description: the value assigned + type: string + type: object +host: localhost:8080 +info: + contact: {} + description: The momentum core api manages the core structure of momentum + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: Momentum Core API + version: early-alpha +paths: + /api/beta/applications: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: $ref: '#/definitions/artefacts.Artefact' - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: get a list of deployments for a given stage by id - tags: - - artefacts - /api/beta/file: - post: - consumes: - - application/json - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/files.File' - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: adds a new file to a given parent (triggers transaction) - tags: - - files - /api/beta/file/{id}: - get: - parameters: - - description: file id - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/files.File' - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets the content of a file - tags: - - files - put: - consumes: - - application/json - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/files.File' - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: updates the given file (triggers transaction) - tags: - - files - /api/beta/file/{id}/line/{lineNumber}/overwritten-by: - get: - parameters: - - description: file id - in: path - name: id - required: true - type: string - - description: line number in file - in: path - name: lineNumber - required: true - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/files.Overwrite' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets a list of overwrites which overwrite the given line. - tags: - - files - /api/beta/stages: - get: - parameters: - - description: application or stage id - in: query - name: parentId - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/artefacts.Artefact' - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets a list of all stages within an application or stage by id. - tags: - - artefacts - /api/beta/templates/applications: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/templates.Template' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets all available application templates - tags: - - templates - /api/beta/templates/deployments: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/templates.Template' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets all available deployment templates - tags: - - templates - /api/beta/templates/stages: - get: - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/templates.Template' - type: array - "400": - description: Bad Request - schema: - $ref: '#/definitions/config.ApiError' - "404": - description: Not Found - schema: - $ref: '#/definitions/config.ApiError' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/config.ApiError' - summary: gets all available stage templates - tags: - - templates - schemes: - - http - swagger: "2.0" + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets a list of all applications + tags: + - artefacts + /api/beta/artefact/{id}/: + get: + parameters: + - description: artefact id + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/artefacts.Artefact' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: get an artefact by id (an artefact can be any of APPLICATION, STAGE + or DEPLOYMENT) + tags: + - artefacts + /api/beta/deployments: + get: + parameters: + - description: stage id + in: query + name: stageId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/artefacts.Artefact' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: get a list of deployments for a given stage by id + tags: + - artefacts + /api/beta/file: + post: + consumes: + - application/json + parameters: + - description: the body shall contain a File instance + in: body + name: CreateFileRequest + required: true + schema: + $ref: '#/definitions/files.CreateFileRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/files.File' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: adds a new file to a given parent (triggers transaction) + tags: + - files + /api/beta/file/{id}: + get: + parameters: + - description: file id + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/files.File' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets the content of a file + tags: + - files + put: + consumes: + - application/json + parameters: + - description: file id + in: path + name: id + required: true + type: string + - description: the body shall contain a File instance + in: body + name: File + required: true + schema: + $ref: '#/definitions/files.File' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/files.File' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: updates the given file (triggers transaction) + tags: + - files + /api/beta/file/{id}/line/{lineNumber}/overwritten-by: + get: + parameters: + - description: file id + in: path + name: id + required: true + type: string + - description: line number in file + in: path + name: lineNumber + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/overwrites.Overwrite' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets a list of overwrites which overwrite the given line. + tags: + - files + /api/beta/stages: + get: + parameters: + - description: application or stage id + in: query + name: parentId + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/artefacts.Artefact' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets a list of all stages within an application or stage by id. + tags: + - artefacts + /api/beta/templates: + post: + consumes: + - application/json + parameters: + - description: the body shall contain a CreateTemplateRequest instance + in: body + name: CreateTemplateRequest + required: true + schema: + $ref: '#/definitions/templates.CreateTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/templates.Template' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: adds a new template (triggers transaction) + tags: + - templates + /api/beta/templates/applications: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets all available application templates + tags: + - templates + /api/beta/templates/deployments: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets all available deployment templates + tags: + - templates + /api/beta/templates/spec/:templateName: + get: + parameters: + - description: name of the template (template names are unique) + in: path + name: templateName + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/templates.Template' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets the spec for a template, which contains values to be set when + applying the template + tags: + - templates + /api/beta/templates/spec/apply/:anchorArtefactId: + post: + consumes: + - application/json + parameters: + - description: id of the artefact where the template shall be applied. Must + be a directory. + in: path + name: anchorArtefactId + required: true + type: string + - description: the body shall contain a CreateTemplateRequest instance + in: body + name: TemplateSpec + required: true + schema: + $ref: '#/definitions/templates.TemplateSpec' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/templates.Template' + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets the spec for a template, which contains values to be set when + applying the template (triggers transaction) + tags: + - templates + /api/beta/templates/stages: + get: + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + type: string + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/config.ApiError' + "404": + description: Not Found + schema: + $ref: '#/definitions/config.ApiError' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/config.ApiError' + summary: gets all available stage templates + tags: + - templates +schemes: +- http +swagger: "2.0" diff --git a/momentum-core/files/files.go b/momentum-core/files/files.go index 5d8668a..9d9dfed 100644 --- a/momentum-core/files/files.go +++ b/momentum-core/files/files.go @@ -18,7 +18,7 @@ func fileToBase64(path string) (string, error) { return base64.RawStdEncoding.EncodeToString([]byte(fileAsString)), nil } -func fileToRaw(base64Encoded string) (string, error) { +func FileToRaw(base64Encoded string) (string, error) { bytes, err := base64.RawStdEncoding.DecodeString(base64Encoded) if err != nil { diff --git a/momentum-core/files/routes.go b/momentum-core/files/routes.go index f6f850e..9eed50f 100644 --- a/momentum-core/files/routes.go +++ b/momentum-core/files/routes.go @@ -56,7 +56,8 @@ func GetFile(c *gin.Context) { // @Tags files // @Accept json // @Produce json -// @Body CreateFileRequest +// @Body json +// @Param CreateFileRequest body CreateFileRequest true "the body shall contain a File instance" // @Success 200 {object} File // @Failure 400 {object} config.ApiError // @Failure 404 {object} config.ApiError @@ -90,7 +91,7 @@ func AddFile(c *gin.Context) { return } - fileContentDecoded, err := fileToRaw(createFileReq.Body) + fileContentDecoded, err := FileToRaw(createFileReq.Body) if err != nil { c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) config.LOGGER.LogError(err.Error(), err, traceId) @@ -156,7 +157,9 @@ func AddFile(c *gin.Context) { // @Tags files // @Accept json // @Produce json -// @Body File +// @Body json +// @Param id path string true "file id" +// @Param File body File true "the body shall contain a File instance" // @Success 200 {object} File // @Failure 400 {object} config.ApiError // @Failure 404 {object} config.ApiError @@ -174,7 +177,7 @@ func UpdateFile(c *gin.Context) { return } - decodedBody, err := fileToRaw(requestedFile.Body) + decodedBody, err := FileToRaw(requestedFile.Body) if err != nil { c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) config.LOGGER.LogError(err.Error(), err, traceId) diff --git a/momentum-core/overwrites/overwrites.go b/momentum-core/overwrites/overwrites.go index 3e8fb44..f963780 100644 --- a/momentum-core/overwrites/overwrites.go +++ b/momentum-core/overwrites/overwrites.go @@ -15,6 +15,7 @@ type OverwriteProvider func(origin *artefacts.Artefact, originLineNumber int) ([ var ActiveOverwriteProviders []OverwriteProvider = []OverwriteProvider{defaultOverwriteBehaviour, ruleEngineOverwriteBehaviour} func OverwriteConfigByArtefact(artefactId string) *OverwriteConfig { + return nil } diff --git a/momentum-core/templates/encoder.go b/momentum-core/templates/encoder.go new file mode 100644 index 0000000..6072b27 --- /dev/null +++ b/momentum-core/templates/encoder.go @@ -0,0 +1,41 @@ +package templates + +import ( + "momentum-core/utils" + "os" + + "gopkg.in/yaml.v3" +) + +func templateConfigFromFile(path string) (*TemplateConfig, error) { + + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + conf := new(TemplateConfig) + err = yaml.NewDecoder(f).Decode(conf) + if err != nil { + return nil, err + } + + return conf, nil +} + +func templateConfigToFile(path string, rules *TemplateConfig) (string, error) { + + f, err := utils.FileOpen(path, os.O_CREATE|os.O_WRONLY) + if err != nil { + return "", err + } + defer f.Close() + + err = yaml.NewEncoder(f).Encode(rules) + if err != nil { + return "", err + } + + return path, nil +} diff --git a/momentum-core/templates/example-template.json b/momentum-core/templates/example-template.json new file mode 100644 index 0000000..b5c353b --- /dev/null +++ b/momentum-core/templates/example-template.json @@ -0,0 +1,36 @@ +{ + "template": { + "directories": [ + { + "directories": [], + "files": [ + { + "name": "values.yaml", + "templateBody": "aToKICBtOiBhIGR1bW15" + }, + { + "name": "secret.yaml", + "templateBody": "aToKICBtOiBhIGR1bW15" + } + ], + "name": "_base" + } + ], + "files": [ + { + "name": "kustomization.yaml", + "templateBody": "aToKICBtOiBhIGR1bW15" + }, + { + "name": "ns.yaml", + "templateBody": "aToKICBtOiBhIGR1bW15" + } + ], + "name": "test-app-template" + }, + "templateConfig": { + "children": [], + "kind": 1 + }, + "templateKind": 1 + } \ No newline at end of file diff --git a/momentum-core/templates/model.go b/momentum-core/templates/model.go index b3a6239..54d4df5 100644 --- a/momentum-core/templates/model.go +++ b/momentum-core/templates/model.go @@ -24,30 +24,36 @@ type TemplateFile struct { TemplateBody string `json:"templateBody"` // base64 encoded } +type TemplateConfig struct { + Kind TemplateKind `json:"kind" yaml:"kind"` + Children []string `json:"children" yaml:"children"` +} + type CreateTemplateRequest struct { TemplateKind TemplateKind `json:"templateKind"` // the toplevel directories name is the name of the template Template *TemplateDir `json:"template"` - Children *Template `json:"children"` - OverwriteConfig *overwrites.OverwriteConfig `json:"overwriteConfig"` + TemplateConfig *TemplateConfig `json:"templateConfig"` + OverwriteConfig *overwrites.OverwriteConfig `json:"-"` // `json:"overwriteConfig"` // TODO: implemented later } type Template struct { // be aware, that each template must have an unique name // it doesn't matter if they are of different template kind - TemplateName string `json:"templateName"` - TemplateKind TemplateKind `json:"templateKind"` - Template *files.Dir `json:"template"` + Name string `json:"name"` + Kind TemplateKind `json:"kind"` + Root *files.Dir `json:"root"` // The children are templates which are contained within the template. Children []*Template `json:"children"` } type TemplateSpec struct { + Template *Template `json:"template"` ValueSpecs []*ValueSpec `json:"valueSpecs"` } type ValueSpec struct { - TemplateId string `json:"templateId"` // id of the template which the value belongs to - Name string `json:"name"` // name of the value (name displayed in frontend) - Value string `json:"value"` // the value assigned + TemplateName string `json:"templateName"` // name of the template which the value belongs to + Name string `json:"name"` // name of the value (name displayed in frontend) + Value string `json:"value"` // the value assigned } diff --git a/momentum-core/templates/routes.go b/momentum-core/templates/routes.go index b845762..813cfaf 100644 --- a/momentum-core/templates/routes.go +++ b/momentum-core/templates/routes.go @@ -1,9 +1,11 @@ package templates import ( + "errors" "momentum-core/config" "net/http" + gittransaction "github.com/Joel-Haeberli/git-transaction" "github.com/gin-gonic/gin" ) @@ -11,6 +13,9 @@ func RegisterTemplateRoutes(engine *gin.Engine) { engine.GET(config.API_TEMPLATES_APPLICATIONS, GetApplicationTemplates) engine.GET(config.API_TEMPLATES_STAGES, GetStageTemplates) engine.GET(config.API_TEMPLATES_DEPLOYMENTS, GetDeploymentTemplates) + engine.POST(config.API_TEMPLATES_ADD, AddTemplate) + engine.GET(config.API_TEMPLATE_GET_SPEC, GetTemplateSpec) + engine.POST(config.API_TEMPLATE_APPLY, ApplyDeploymentTemplate) } // GetApplicationTemplates godoc @@ -18,7 +23,7 @@ func RegisterTemplateRoutes(engine *gin.Engine) { // @Summary gets all available application templates // @Tags templates // @Produce json -// @Success 200 {array} Template +// @Success 200 {array} string // @Failure 400 {object} config.ApiError // @Failure 404 {object} config.ApiError // @Failure 500 {object} config.ApiError @@ -34,7 +39,7 @@ func GetApplicationTemplates(c *gin.Context) { // @Summary gets all available stage templates // @Tags templates // @Produce json -// @Success 200 {array} Template +// @Success 200 {array} string // @Failure 400 {object} config.ApiError // @Failure 404 {object} config.ApiError // @Failure 500 {object} config.ApiError @@ -50,7 +55,7 @@ func GetStageTemplates(c *gin.Context) { // @Summary gets all available deployment templates // @Tags templates // @Produce json -// @Success 200 {array} Template +// @Success 200 {array} string // @Failure 400 {object} config.ApiError // @Failure 404 {object} config.ApiError // @Failure 500 {object} config.ApiError @@ -61,12 +66,92 @@ func GetDeploymentTemplates(c *gin.Context) { c.JSON(http.StatusOK, result) } +// AddTemplate godoc +// +// @Summary adds a new template (triggers transaction) +// @Tags templates +// @Accept json +// @Produce json +// @Body json +// @Param CreateTemplateRequest body CreateTemplateRequest true "the body shall contain a CreateTemplateRequest instance" +// @Success 200 {object} Template +// @Failure 400 {object} config.ApiError +// @Failure 404 {object} config.ApiError +// @Failure 500 {object} config.ApiError +// @Router /api/beta/templates [post] +func AddTemplate(c *gin.Context) { + + traceId := config.LOGGER.TraceId() + + request := new(CreateTemplateRequest) + err := c.BindJSON(request) + if err != nil { + c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + if TemplateExists(request.Template.Name) { + err = errors.New("template with this name already exists") + c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + if len(request.Template.Directories) == 0 && len(request.Template.Files) == 0 { + err = errors.New("expecting non empty template") + c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + validConfig, err := validTemplateConfig(request.TemplateConfig, request.TemplateKind) + if !validConfig { + c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + ctx, transaction, err := gittransaction.New(config.TRANSACTION_MODE, config.GLOBAL.RepoDir(), config.GLOBAL.TransactionToken()) + if err != nil { + transaction.Rollback(ctx) + c.JSON(http.StatusInternalServerError, config.NewApiError(err, http.StatusInternalServerError, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + temp, err := CreateTemplate(request) + if err != nil { + if err != nil { + c.JSON(http.StatusBadRequest, config.NewApiError(err, http.StatusBadRequest, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + } + + err = transaction.Write(ctx) + if err != nil { + c.JSON(http.StatusInternalServerError, config.NewApiError(err, http.StatusInternalServerError, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + err = transaction.Commit(ctx) + if err != nil { + c.JSON(http.StatusInternalServerError, config.NewApiError(err, http.StatusInternalServerError, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + c.JSON(http.StatusOK, temp) +} + // GetTemplateSpec godoc // // @Summary gets the spec for a template, which contains values to be set when applying the template // @Tags templates // @Produce json -// @Param id path string true "file id" +// @Param templateName path string true "name of the template (template names are unique)" // @Success 200 {object} Template // @Failure 400 {object} config.ApiError // @Failure 404 {object} config.ApiError @@ -74,8 +159,50 @@ func GetDeploymentTemplates(c *gin.Context) { // @Router /api/beta/templates/spec/:templateName [get] func GetTemplateSpec(c *gin.Context) { + c.JSON(http.StatusNoContent, config.NewApiError(errors.New("not implemented yet"), http.StatusNoContent, c, config.LOGGER.TraceId())) } +// ApplyDeploymentTemplate godoc +// +// @Summary gets the spec for a template, which contains values to be set when applying the template (triggers transaction) +// @Tags templates +// @Accept json +// @Produce json +// @Body json +// @Param anchorArtefactId path string true "id of the artefact where the template shall be applied. Must be a directory." +// @Param TemplateSpec body TemplateSpec true "the body shall contain a CreateTemplateRequest instance" +// @Success 200 {object} Template +// @Failure 400 {object} config.ApiError +// @Failure 404 {object} config.ApiError +// @Failure 500 {object} config.ApiError +// @Router /api/beta/templates/spec/apply/:anchorArtefactId [post] func ApplyDeploymentTemplate(c *gin.Context) { + traceId := config.LOGGER.TraceId() + + ctx, transaction, err := gittransaction.New(config.TRANSACTION_MODE, config.GLOBAL.RepoDir(), config.GLOBAL.TransactionToken()) + if err != nil { + transaction.Rollback(ctx) + c.JSON(http.StatusInternalServerError, config.NewApiError(err, http.StatusInternalServerError, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + // TODO APPLY TEMPLATE HERE + + err = transaction.Write(ctx) + if err != nil { + c.JSON(http.StatusInternalServerError, config.NewApiError(err, http.StatusInternalServerError, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + err = transaction.Commit(ctx) + if err != nil { + c.JSON(http.StatusInternalServerError, config.NewApiError(err, http.StatusInternalServerError, c, traceId)) + config.LOGGER.LogError(err.Error(), err, traceId) + return + } + + c.JSON(http.StatusNoContent, config.NewApiError(errors.New("not implemented yet"), http.StatusNoContent, c, config.LOGGER.TraceId())) } diff --git a/momentum-core/templates/templates.go b/momentum-core/templates/templates.go index da02e15..164e7ce 100644 --- a/momentum-core/templates/templates.go +++ b/momentum-core/templates/templates.go @@ -1,20 +1,110 @@ package templates import ( + "errors" "io/fs" "momentum-core/config" + "momentum-core/files" "momentum-core/utils" "os" + "path/filepath" "strings" ) +const TEMPLATE_CONFIG_FILENAME = "momentum-template-config.yaml" + func LoadSpec(templateName string, templateKind TemplateKind) []*TemplateSpec { return make([]*TemplateSpec, 0) } -func CreateTemplate(templateRequest *CreateTemplateRequest) { +func LoadTemplate(templateName string) (*Template, error) { + + if !TemplateExists(templateName) { + return nil, errors.New("no template name '" + templateName + "'") + } + + //files. + + template := new(Template) + //template.Kind = + + return template, nil +} + +func CreateTemplate(templateRequest *CreateTemplateRequest) (*Template, error) { + + templateAnchorPath := filepath.Join(templatePathForKind(templateRequest.TemplateKind)) + + err := createTemplateDir(templateAnchorPath, templateRequest.Template) + if err != nil { + return nil, err + } + + _, err = templateConfigToFile(filepath.Join(templateAnchorPath, templateRequest.Template.Name, TEMPLATE_CONFIG_FILENAME), templateRequest.TemplateConfig) + if err != nil { + return nil, err + } + + return LoadTemplate(templateRequest.Template.Name) +} + +func createTemplateDir(anchorPath string, templateDir *TemplateDir) error { + + templateAnchorPath := filepath.Join(anchorPath, templateDir.Name) + err := utils.DirCreate(templateAnchorPath) + if err != nil { + return err + } + + for _, dir := range templateDir.Directories { + err := createTemplateDir(templateAnchorPath, dir) + if err != nil { + return err + } + } + + for _, file := range templateDir.Files { + err := createTemplateFile(templateAnchorPath, file) + if err != nil { + return err + } + } + + return nil +} + +func createTemplateFile(anchorPath string, templateFile *TemplateFile) error { + if !strings.HasSuffix(templateFile.Name, ".yaml") && !strings.HasSuffix(templateFile.Name, ".yml") { + return errors.New("only yaml files supported at the moment (you sent " + templateFile.Name + ")") + } + + path := filepath.Join(anchorPath, templateFile.Name) + body, err := files.FileToRaw(templateFile.TemplateBody) + if err != nil { + return err + } + + success := utils.FileWrite(path, body) + if !success { + return errors.New("unable to write file '" + path + "'") + } + return nil +} + +func templatePathForKind(kind TemplateKind) string { + + switch kind { + case APPLICATION: + return config.ApplicationTemplatesPath(config.GLOBAL) + case STAGE: + return config.StageTemplatesPath(config.GLOBAL) + case DEPLOYMENT: + return config.DeploymentTemplatesPath(config.GLOBAL) + default: + return "" + } } func TemplateNames(path string) []string { @@ -37,6 +127,23 @@ func TemplateExists(name string) bool { return applicationExists(name) || stageExists(name) || deploymentExists(name) } +// validates a given config. A config is valid if all contained children are present. +func validTemplateConfig(templateConfig *TemplateConfig, templateKind TemplateKind) (bool, error) { + + if templateKind != templateConfig.Kind { + return false, errors.New("template config must have same kind as request") + } + + for _, temp := range templateConfig.Children { + + if !TemplateExists(temp) { + return false, errors.New("template with name '" + temp + "' does not exist") + } + } + + return true, nil +} + func applicationExists(name string) bool { appTemplates := config.ApplicationTemplatesPath(config.GLOBAL) diff --git a/momentum-core/utils/gin-body-extractor.go b/momentum-core/utils/gin-body-extractor.go deleted file mode 100644 index e713651..0000000 --- a/momentum-core/utils/gin-body-extractor.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -import "github.com/gin-gonic/gin" - -func Extract[T any](c *gin.Context) (*T, error) { - - t := new(T) - err := c.BindJSON(t) - if err != nil { - return nil, err - } - return t, nil -} diff --git a/momentum-core/yaml/tree.go b/momentum-core/yaml/tree.go index 5d6f7f7..a463a11 100644 --- a/momentum-core/yaml/tree.go +++ b/momentum-core/yaml/tree.go @@ -3,6 +3,7 @@ package yaml import ( "errors" "fmt" + "momentum-core/config" "momentum-core/utils" "strings" ) @@ -56,7 +57,7 @@ func NewNode(kind NodeKind, path string, value string, parent *ViewNode, childre n.IsWrapping = true } - id, err := utils.GenerateId(n.FullPath()) + id, err := utils.GenerateId(config.IdGenerationPath(n.FullPath())) if err != nil { fmt.Println("generating id failed:", err.Error()) }