cmd/hub/api/template.go (417 lines of code) (raw):
// Copyright (c) 2022 EPAM Systems, Inc.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//go:build api
package api
import (
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/url"
"os"
"strings"
"github.com/epam/hubctl/cmd/hub/config"
"github.com/epam/hubctl/cmd/hub/util"
)
const templatesResource = "hub/api/v1/templates"
//lint:ignore U1000 still needed?
var templatesCache = make(map[string]*StackTemplate)
func Templates(selector string, showSecrets, showGitRemote, wildcardSecret, showGitStatus, jsonFormat bool) {
templates, err := templatesBy(selector)
if err != nil {
log.Fatalf("Unable to query for Template(s): %v", err)
}
if len(templates) == 0 {
if jsonFormat {
log.Print("No Templates")
} else {
fmt.Print("No Templates\n")
}
} else {
if jsonFormat {
var toMarshal interface{}
if len(templates) == 1 {
toMarshal = &templates[0]
} else {
toMarshal = templates
}
out, err := json.MarshalIndent(toMarshal, "", " ")
if err != nil {
log.Fatalf("Error marshalling JSON response for output: %v", err)
}
os.Stdout.Write(out)
os.Stdout.Write([]byte("\n"))
} else {
deploymentKey := ""
if showGitRemote {
if wildcardSecret {
key, err := userDeploymentKey("")
if err != nil {
log.Fatalf("Unable to retrieve deployment key: %v", err)
}
deploymentKey = key
}
} else {
fmt.Print("Templates:\n")
}
errors := make([]error, 0)
for _, template := range templates {
if showGitRemote {
if !wildcardSecret {
key, err := userDeploymentKey("git:" + template.Id)
if err != nil {
errors = append(errors, fmt.Errorf("Unable to retrieve deployment key: %v", err))
deploymentKey = "(error)"
} else {
deploymentKey = key
}
}
title := ""
if len(templates) > 1 {
title = fmt.Sprintf("%s [%s]: ", template.Name, template.Id)
}
fmt.Printf("%s%s\n", title, formatGitRemoteWithKey(template.GitRemote.Public, deploymentKey))
} else {
errors = formatTemplateEntity(&template, showSecrets, showGitStatus, errors)
}
}
if len(errors) > 0 {
fmt.Print("Errors encountered:\n")
for _, err := range errors {
fmt.Printf("\t%v\n", err)
}
}
}
}
}
func formatGitRemoteWithKey(url, key string) string {
i := strings.Index(url, "://")
if i > 0 && i < len(url)-3 {
return fmt.Sprintf("%s://%s@%s", url[0:i], key, url[i+3:])
}
return url
}
func formatTemplateEntity(template *StackTemplate, showSecrets, showGitStatus bool, errors []error) []error {
title := formatTemplateTitle(template)
if template.Description != "" {
title = fmt.Sprintf("%s - %s", title, template.Description)
}
fmt.Printf("\n\t%s\n", title)
if len(template.Tags) > 0 {
fmt.Printf("\t\tTags: %s\n", strings.Join(template.Tags, ", "))
}
fmt.Printf("\t\tStatus: %s\n", template.Status)
if template.Stack != nil && template.Stack.Name != "" {
fmt.Printf("\t\tStack: %s\n", formatStackRef(template.Stack))
}
if template.Component != nil && template.Component.Name != "" {
fmt.Printf("\t\tComponent: %s\n", formatComponentRef(template.Component))
}
if len(template.Verbs) > 0 {
fmt.Printf("\t\tVerbs: %s\n", strings.Join(template.Verbs, ", "))
}
if len(template.ComponentsEnabled) > 0 {
fmt.Printf("\t\tComponents enabled: %s\n", strings.Join(template.ComponentsEnabled, ", "))
}
if len(template.TeamsPermissions) > 0 {
formatted := formatTeams(template.TeamsPermissions)
fmt.Printf("\t\tTeams: %s\n", formatted)
}
if template.GitRemote.Public != "" {
fmt.Printf("\t\tGit: %s\n", template.GitRemote.Public)
if showGitStatus {
g, err := templateGitStatus(template.Id)
if err != nil {
errors = append(errors, err)
} else {
commit := g.Commit
if len(commit) > 7 {
commit = commit[:7]
}
fmt.Printf("\t\t\t%s %s %s %s %s\n", commit, g.Ref, g.Author, g.Date, g.Subject)
}
}
}
if len(template.Parameters) > 0 {
fmt.Print("\t\tParameters:\n")
}
resource := fmt.Sprintf("%s/%s", templatesResource, template.Id)
for _, param := range sortParameters(template.Parameters) {
formatted, err := formatParameter(resource, param, showSecrets)
fmt.Printf("\t\t%s\n", formatted)
if err != nil {
errors = append(errors, err)
}
}
return errors
}
func formatTemplateTitle(template *StackTemplate) string {
return fmt.Sprintf("%s [%s]", template.Name, template.Id)
}
func formatTemplateRef(ref *StackTemplateRef) string {
return fmt.Sprintf("%s [%s]", ref.Name, ref.Id)
}
func formatStackRef(ref *StackRef) string {
return fmt.Sprintf("%s [%s]", ref.Name, ref.Id)
}
func formatComponentRef(ref *ComponentRef) string {
return fmt.Sprintf("%s [%s]", ref.Name, ref.Id)
}
func formatTemplate(template *StackTemplate) {
errors := formatTemplateEntity(template, false, false, make([]error, 0))
if len(errors) > 0 {
fmt.Print("Errors encountered formatting response:\n")
for _, err := range errors {
fmt.Printf("\t%v\n", err)
}
}
}
//lint:ignore U1000 still needed?
func cachedTemplateBy(selector string) (*StackTemplate, error) {
template, cached := templatesCache[selector]
if !cached {
var err error
template, err = templateBy(selector)
if err != nil {
return nil, err
}
templatesCache[selector] = template
}
return template, nil
}
func templateBy(selector string) (*StackTemplate, error) {
if !util.IsUint(selector) {
return templateByName(selector)
}
return templateById(selector)
}
func templatesBy(selector string) ([]StackTemplate, error) {
if !util.IsUint(selector) {
return templatesByName(selector)
}
template, err := templateById(selector)
if err != nil {
return nil, err
}
if template != nil {
return []StackTemplate{*template}, nil
}
return nil, nil
}
func templateById(id string) (*StackTemplate, error) {
path := fmt.Sprintf("%s/%s", templatesResource, url.PathEscape(id))
var jsResp StackTemplate
code, err := get(hubApi(), path, &jsResp)
if code == 404 {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("Error querying HubCTL Templates: %v", err)
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP querying HubCTL Templates, expected 200 HTTP", code)
}
return &jsResp, nil
}
func templateByName(name string) (*StackTemplate, error) {
templates, err := templatesByName(name)
if err != nil {
return nil, fmt.Errorf("Unable to query for Template `%s`: %v", name, err)
}
if len(templates) == 0 {
return nil, fmt.Errorf("No Template `%s` found", name)
}
if len(templates) > 1 {
return nil, fmt.Errorf("More than one Template returned by name `%s`", name)
}
template := templates[0]
return &template, nil
}
func templatesByName(name string) ([]StackTemplate, error) {
path := templatesResource
if name != "" {
path += "?name=" + url.QueryEscape(name)
}
var jsResp []StackTemplate
code, err := get(hubApi(), path, &jsResp)
if code == 404 {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("Error querying HubCTL Templates: %v", err)
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP querying HubCTL Templates, expected 200 HTTP", code)
}
return jsResp, nil
}
func templateGitStatus(id string) (*TemplateStatus, error) {
path := fmt.Sprintf("%s/%s/git/status", templatesResource, url.PathEscape(id))
var jsResp TemplateStatus
code, err := get(hubApi(), path, &jsResp)
if code == 404 {
return nil, nil
}
if err != nil {
return nil, fmt.Errorf("Error querying HubCTL Template Git status: %v", err)
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP querying HubCTL Template Git status, expected 200 HTTP", code)
}
return &jsResp, nil
}
func CreateTemplate(req StackTemplateRequest) {
template, err := createTemplate(req)
if err != nil {
log.Fatalf("Unable to create HubCTL Template: %v", err)
}
formatTemplate(template)
}
func createTemplate(req StackTemplateRequest) (*StackTemplate, error) {
var jsResp StackTemplate
code, err := post(hubApi(), templatesResource, &req, &jsResp)
if err != nil {
return nil, err
}
if code != 200 && code != 201 {
return nil, fmt.Errorf("Got %d HTTP creating HubCTL Template, expected [200, 201] HTTP", code)
}
return &jsResp, nil
}
func RawCreateTemplate(body io.Reader) {
template, err := rawCreateTemplate(body)
if err != nil {
log.Fatalf("Unable to create HubCTL Template: %v", err)
}
formatTemplate(template)
}
func rawCreateTemplate(body io.Reader) (*StackTemplate, error) {
var jsResp StackTemplate
code, err := post2(hubApi(), templatesResource, body, &jsResp)
if err != nil {
return nil, err
}
if code != 200 && code != 201 {
return nil, fmt.Errorf("Got %d HTTP creating HubCTL Template, expected [200, 201] HTTP", code)
}
return &jsResp, nil
}
func InitTemplate(selector string) {
err := initTemplate(selector)
if err != nil {
log.Fatalf("Unable to initialize HubCTL Template: %v", err)
}
}
func initTemplate(selector string) error {
template, err := templateBy(selector)
if err != nil {
return err
}
if template == nil {
return error404
}
path := fmt.Sprintf("%s/%s/git/create", templatesResource, url.PathEscape(template.Id))
code, err := post2(hubApiLongWait(), path, nil, nil)
if err != nil {
return err
}
if code != 202 && code != 204 {
return fmt.Errorf("Got %d HTTP initializing HubCTL Template, expected [202, 204] HTTP", code)
}
return nil
}
func DeleteTemplate(selector string) {
err := deleteTemplate(selector)
if err != nil {
log.Fatalf("Unable to delete HubCTL Template: %v", err)
}
}
func deleteTemplate(selector string) error {
template, err := templateBy(selector)
id := ""
if err != nil {
str := err.Error()
if util.IsUint(selector) &&
(strings.Contains(str, "json: cannot unmarshal") || strings.Contains(str, "cannot parse") || config.Force) {
util.Warn("%v", err)
id = selector
} else {
return err
}
} else if template == nil {
return error404
} else {
id = template.Id
}
force := ""
if config.Force {
force = "?force=true"
}
path := fmt.Sprintf("%s/%s%s", templatesResource, url.PathEscape(id), force)
code, err := delete(hubApi(), path)
if err != nil {
return err
}
if code != 202 && code != 204 {
return fmt.Errorf("Got %d HTTP deleting HubCTL Template, expected [202, 204] HTTP", code)
}
return nil
}
func PatchTemplate(selector string, change StackTemplatePatch) {
template, err := patchTemplate(selector, change)
if err != nil {
log.Fatalf("Unable to patch HubCTL Template: %v", err)
}
formatTemplate(template)
}
func patchTemplate(selector string, change StackTemplatePatch) (*StackTemplate, error) {
template, err := templateBy(selector)
if err != nil {
return nil, err
}
if template == nil {
return nil, error404
}
path := fmt.Sprintf("%s/%s", templatesResource, url.PathEscape(template.Id))
var jsResp StackTemplate
code, err := patch(hubApi(), path, &change, &jsResp)
if err != nil {
return nil, err
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP patching HubCTL Template, expected 200 HTTP", code)
}
return &jsResp, nil
}
func RawPatchTemplate(selector string, body io.Reader) {
template, err := rawPatchTemplate(selector, body)
if err != nil {
log.Fatalf("Unable to patch HubCTL Template: %v", err)
}
formatTemplate(template)
}
func rawPatchTemplate(selector string, body io.Reader) (*StackTemplate, error) {
template, err := templateBy(selector)
if err != nil && !config.Force {
return nil, err
}
if template == nil && !config.Force {
return nil, error404
}
templateId := ""
if template == nil {
if util.IsUint(selector) {
templateId = selector
} else {
return nil, errors.New("Specify template by Id")
}
} else {
templateId = template.Id
}
path := fmt.Sprintf("%s/%s", templatesResource, url.PathEscape(templateId))
var jsResp StackTemplate
code, err := patch2(hubApi(), path, body, &jsResp)
if err != nil {
return nil, err
}
if code != 200 {
return nil, fmt.Errorf("Got %d HTTP patching HubCTL Template, expected 200 HTTP", code)
}
return &jsResp, nil
}