pkg/cmdhelpers/workflow_helpers.go (142 lines of code) (raw):
package cmdhelpers
import (
"errors"
"fmt"
"io/fs"
"os"
"path"
"gopkg.in/yaml.v3"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/kubernetes/scheme"
log "github.com/sirupsen/logrus"
"github.com/Azure/draft/pkg/config"
"github.com/Azure/draft/pkg/osutil"
"github.com/Azure/draft/pkg/templatewriter"
)
type Workflows struct {
workflows map[string]fs.DirEntry
configs map[string]*config.DraftConfig
Dest string
workflowTemplates fs.FS
}
const (
parentDirName = "workflows"
configFileName = "draft.yaml"
emptyDefaultFlagValue = ""
)
func UpdateProductionDeployments(deployType, dest string, draftConfig *config.DraftConfig, templateWriter templatewriter.TemplateWriter) error {
acr, err := draftConfig.GetVariable("AZURECONTAINERREGISTRY")
if err != nil {
return fmt.Errorf("get variable: %w", err)
}
containerName, err := draftConfig.GetVariable("CONTAINERNAME")
if err != nil {
return fmt.Errorf("get variable: %w", err)
}
productionImage := fmt.Sprintf("%s.azurecr.io/%s", acr.Value, containerName.Value)
switch deployType {
case "helm":
return setHelmContainerImage(dest+"/charts/production.yaml", productionImage, templateWriter)
case "kustomize":
return setDeploymentContainerImage(dest+"/overlays/production/deployment.yaml", productionImage)
case "manifests":
return setDeploymentContainerImage(dest+"/manifests/deployment.yaml", productionImage)
}
return nil
}
func setDeploymentContainerImage(filePath, productionImage string) error {
decode := scheme.Codecs.UniversalDeserializer().Decode
file, err := os.ReadFile(filePath)
if err != nil {
return err
}
k8sObj, _, err := decode(file, nil, nil)
if err != nil {
return err
}
deploy, ok := k8sObj.(*appsv1.Deployment)
if !ok {
return errors.New("could not decode kubernetes deployment")
}
if len(deploy.Spec.Template.Spec.Containers) != 1 {
return errors.New("unsupported number of containers defined in the deployment spec")
}
deploy.Spec.Template.Spec.Containers[0].Image = productionImage
printer := printers.YAMLPrinter{}
out, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return nil
}
defer func() {
if err := out.Close(); err != nil {
log.Errorf("error closing file: %v", err)
}
}()
return printer.PrintObj(deploy, out)
}
func setHelmContainerImage(filePath, productionImage string, templateWriter templatewriter.TemplateWriter) error {
file, err := os.ReadFile(filePath)
if err != nil {
return err
}
var deploy HelmProductionYaml
err = yaml.Unmarshal(file, &deploy)
if err != nil {
return err
}
deploy.Image.Repository = productionImage
out, err := yaml.Marshal(deploy)
if err != nil {
return err
}
return templateWriter.WriteFile(filePath, out)
}
func (w *Workflows) loadConfig(deployType string) (*config.DraftConfig, error) {
val, ok := w.workflows[deployType]
if !ok {
return nil, fmt.Errorf("deploy type %s unsupported", deployType)
}
configPath := path.Join(parentDirName, val.Name(), configFileName)
configBytes, err := fs.ReadFile(w.workflowTemplates, configPath)
if err != nil {
return nil, err
}
var draftConfig config.DraftConfig
if err = yaml.Unmarshal(configBytes, &draftConfig); err != nil {
return nil, err
}
return &draftConfig, nil
}
func (w *Workflows) GetConfig(deployType string) (*config.DraftConfig, error) {
val, ok := w.configs[deployType]
if !ok {
return nil, fmt.Errorf("deploy type %s unsupported", deployType)
}
return val, nil
}
func (w *Workflows) populateConfigs() {
for deployType := range w.workflows {
draftConfig, err := w.loadConfig(deployType)
if err != nil {
log.Debugf("no draftConfig found for workflow of deploy type %s", deployType)
draftConfig = &config.DraftConfig{}
}
w.configs[deployType] = draftConfig
}
}
func (w *Workflows) CreateWorkflowFiles(deployType string, draftConfig *config.DraftConfig, templateWriter templatewriter.TemplateWriter) error {
val, ok := w.workflows[deployType]
if !ok {
return fmt.Errorf("deployment type: %s is not currently supported", deployType)
}
srcDir := path.Join(parentDirName, val.Name())
log.Debugf("source directory for workflow template: %s", srcDir)
if err := draftConfig.ApplyDefaultVariables(); err != nil {
return fmt.Errorf("create workflow files: %w", err)
}
if err := osutil.CopyDirWithTemplates(w.workflowTemplates, srcDir, w.Dest, draftConfig, templateWriter); err != nil {
return err
}
return nil
}