Skip to content

Commit

Permalink
Merge pull request #4543 from werf/feat-external-dependencies
Browse files Browse the repository at this point in the history
feat(helm): external-dependencies for resources
  • Loading branch information
ilya-lesikov committed Jun 16, 2022
2 parents a64d682 + 73e6bcc commit 1c607aa
Show file tree
Hide file tree
Showing 17 changed files with 384 additions and 39 deletions.
10 changes: 8 additions & 2 deletions cmd/werf/bundle/apply/apply.go
Expand Up @@ -214,9 +214,15 @@ func runApply() error {
ChartExtender: bundle,
}

stagesExternalDepsGenerator, err := helm.NewStagesExternalDepsGenerator(actionConfig.RESTClientGetter)
if err != nil {
return fmt.Errorf("error creating external deps generator: %w", err)
}

helmUpgradeCmd, _ := helm_v3.NewUpgradeCmd(actionConfig, logboek.Context(ctx).OutStream(), helm_v3.UpgradeCmdOptions{
StagesSplitter: helm.StagesSplitter{},
ChainPostRenderer: bundle.ChainPostRenderer,
StagesSplitter: helm.NewStagesSplitter(),
StagesExternalDepsGenerator: stagesExternalDepsGenerator,
ChainPostRenderer: bundle.ChainPostRenderer,
ValueOpts: &values.Options{
ValueFiles: common.GetValues(&commonCmdData),
StringValues: common.GetSetString(&commonCmdData),
Expand Down
2 changes: 1 addition & 1 deletion cmd/werf/bundle/render/render.go
Expand Up @@ -221,7 +221,7 @@ func runRender(ctx context.Context) error {
}

helmTemplateCmd, _ := helm_v3.NewTemplateCmd(actionConfig, output, helm_v3.TemplateCmdOptions{
StagesSplitter: helm.StagesSplitter{},
StagesSplitter: helm.NewStagesSplitter(),
ChainPostRenderer: bundle.ChainPostRenderer,
ValueOpts: &values.Options{
ValueFiles: common.GetValues(&commonCmdData),
Expand Down
24 changes: 15 additions & 9 deletions cmd/werf/converge/converge.go
Expand Up @@ -444,15 +444,21 @@ func run(ctx context.Context, containerBackend container_backend.ContainerBacken
return err
}

stagesExternalDepsGenerator, err := helm.NewStagesExternalDepsGenerator(actionConfig.RESTClientGetter)
if err != nil {
return fmt.Errorf("error creating external deps generator: %w", err)
}

helmUpgradeCmd, _ := helm_v3.NewUpgradeCmd(actionConfig, logboek.OutStream(), helm_v3.UpgradeCmdOptions{
StagesSplitter: helm.StagesSplitter{},
ChainPostRenderer: wc.ChainPostRenderer,
ValueOpts: valueOpts,
CreateNamespace: common.NewBool(true),
Install: common.NewBool(true),
Wait: common.NewBool(true),
Atomic: common.NewBool(cmdData.AutoRollback),
Timeout: common.NewDuration(time.Duration(cmdData.Timeout) * time.Second),
StagesSplitter: helm.NewStagesSplitter(),
StagesExternalDepsGenerator: stagesExternalDepsGenerator,
ChainPostRenderer: wc.ChainPostRenderer,
ValueOpts: valueOpts,
CreateNamespace: common.NewBool(true),
Install: common.NewBool(true),
Wait: common.NewBool(true),
Atomic: common.NewBool(cmdData.AutoRollback),
Timeout: common.NewDuration(time.Duration(cmdData.Timeout) * time.Second),
})

return command_helpers.LockReleaseWrapper(ctx, releaseName, lockManager, func() error {
Expand Down Expand Up @@ -525,7 +531,7 @@ func migrateHelm2ToHelm3(ctx context.Context, releaseName, namespace string, mai
}

helmTemplateCmd, _ := helm_v3.NewTemplateCmd(actionConfig, ioutil.Discard, helm_v3.TemplateCmdOptions{
StagesSplitter: helm.StagesSplitter{},
StagesSplitter: helm.NewStagesSplitter(),
ChainPostRenderer: chainPostRenderer,
ValueOpts: valueOpts,
Validate: common.NewBool(true),
Expand Down
2 changes: 1 addition & 1 deletion cmd/werf/dismiss/dismiss.go
Expand Up @@ -232,7 +232,7 @@ func runDismiss(ctx context.Context) error {

dontFailIfNoRelease := true
helmUninstallCmd := helm_v3.NewUninstallCmd(actionConfig, logboek.Context(ctx).OutStream(), helm_v3.UninstallCmdOptions{
StagesSplitter: helm.StagesSplitter{},
StagesSplitter: helm.NewStagesSplitter(),
DeleteNamespace: &cmdData.WithNamespace,
DeleteHooks: &cmdData.WithHooks,
DontFailIfNoRelease: &dontFailIfNoRelease,
Expand Down
6 changes: 4 additions & 2 deletions cmd/werf/helm/helm.go
Expand Up @@ -70,7 +70,7 @@ func NewCmd() *cobra.Command {

cmd.AddCommand(
helm_v3.NewUninstallCmd(actionConfig, os.Stdout, helm_v3.UninstallCmdOptions{
StagesSplitter: helm.StagesSplitter{},
StagesSplitter: helm.NewStagesSplitter(),
}),
helm_v3.NewDependencyCmd(actionConfig, os.Stdout),
helm_v3.NewGetCmd(actionConfig, os.Stdout),
Expand All @@ -80,7 +80,9 @@ func NewCmd() *cobra.Command {
NewTemplateCmd(actionConfig, wc),
helm_v3.NewRepoCmd(os.Stdout),
helm_v3.NewRollbackCmd(actionConfig, os.Stdout, helm_v3.RollbackCmdOptions{
StagesSplitter: helm.StagesSplitter{},
StagesSplitter: helm.NewStagesSplitter(),
// TODO: actionConfig.RESTClientGetter not initialized at this point, but we need it.
StagesExternalDepsGenerator: nil,
}),
NewInstallCmd(actionConfig, wc),
NewUpgradeCmd(actionConfig, wc),
Expand Down
6 changes: 4 additions & 2 deletions cmd/werf/helm/install.go
Expand Up @@ -19,8 +19,10 @@ var installCmdData common.CmdData

func NewInstallCmd(actionConfig *action.Configuration, wc *chart_extender.WerfChartStub) *cobra.Command {
cmd, helmAction := helm_v3.NewInstallCmd(actionConfig, os.Stdout, helm_v3.InstallCmdOptions{
StagesSplitter: helm.StagesSplitter{},
ChainPostRenderer: wc.ChainPostRenderer,
StagesSplitter: helm.NewStagesSplitter(),
// TODO: actionConfig.RESTClientGetter not initialized at this point, but we need it.
StagesExternalDepsGenerator: nil,
ChainPostRenderer: wc.ChainPostRenderer,
})
SetupRenderRelatedWerfChartParams(cmd, &installCmdData)

Expand Down
6 changes: 4 additions & 2 deletions cmd/werf/helm/template.go
Expand Up @@ -17,8 +17,10 @@ var templateCmdData common.CmdData

func NewTemplateCmd(actionConfig *action.Configuration, wc *chart_extender.WerfChartStub) *cobra.Command {
cmd, _ := helm_v3.NewTemplateCmd(actionConfig, os.Stdout, helm_v3.TemplateCmdOptions{
StagesSplitter: helm.StagesSplitter{},
ChainPostRenderer: wc.ChainPostRenderer,
StagesSplitter: helm.NewStagesSplitter(),
// TODO: actionConfig.RESTClientGetter not initialized at this point, but we need it.
StagesExternalDepsGenerator: nil,
ChainPostRenderer: wc.ChainPostRenderer,
})
SetupRenderRelatedWerfChartParams(cmd, &templateCmdData)

Expand Down
6 changes: 4 additions & 2 deletions cmd/werf/helm/upgrade.go
Expand Up @@ -19,8 +19,10 @@ var upgradeCmdData common.CmdData

func NewUpgradeCmd(actionConfig *action.Configuration, wc *chart_extender.WerfChartStub) *cobra.Command {
cmd, _ := helm_v3.NewUpgradeCmd(actionConfig, os.Stdout, helm_v3.UpgradeCmdOptions{
StagesSplitter: helm.StagesSplitter{},
ChainPostRenderer: wc.ChainPostRenderer,
StagesSplitter: helm.NewStagesSplitter(),
// TODO: actionConfig.RESTClientGetter not initialized at this point, but we need it.
StagesExternalDepsGenerator: nil,
ChainPostRenderer: wc.ChainPostRenderer,
})
SetupRenderRelatedWerfChartParams(cmd, &upgradeCmdData)

Expand Down
2 changes: 1 addition & 1 deletion cmd/werf/render/render.go
Expand Up @@ -411,7 +411,7 @@ func runRender(ctx context.Context) error {
}

templateOpts := helm_v3.TemplateCmdOptions{
StagesSplitter: helm.StagesSplitter{},
StagesSplitter: helm.NewStagesSplitter(),
ChainPostRenderer: wc.ChainPostRenderer,
ValueOpts: &values.Options{
ValueFiles: common.GetValues(&commonCmdData),
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -307,6 +307,6 @@ replace k8s.io/helm => github.com/werf/helm v0.0.0-20210202111118-81e74d46da0f

replace github.com/deislabs/oras => github.com/werf/third-party-oras v0.9.1-0.20210927171747-6d045506f4c8

replace helm.sh/helm/v3 => github.com/werf/3p-helm/v3 v3.0.0-20220615081302-d5ffa8d30462
replace helm.sh/helm/v3 => github.com/werf/3p-helm/v3 v3.0.0-20220616090736-b002d47fddea

replace github.com/go-git/go-git/v5 => github.com/ZauberNerd/go-git/v5 v5.4.3-0.20220315170230-29ec1bc1e5db
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -2036,8 +2036,8 @@ github.com/vmware/govmomi v0.20.3/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59b
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg2K1HrLU=
github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
github.com/werf/3p-helm/v3 v3.0.0-20220615081302-d5ffa8d30462 h1:u0njIasP6iKAiFKX7KJwz9/ufgYY+ph04wIWTHvqEPQ=
github.com/werf/3p-helm/v3 v3.0.0-20220615081302-d5ffa8d30462/go.mod h1:NxtE2KObf2PrzDl6SIamPFPKyAqWi10iWuvKlQn/Yao=
github.com/werf/3p-helm/v3 v3.0.0-20220616090736-b002d47fddea h1:qr9t42g4QEasedC84VS7NTltgeOG9OmXEXINckvZD1s=
github.com/werf/3p-helm/v3 v3.0.0-20220616090736-b002d47fddea/go.mod h1:NxtE2KObf2PrzDl6SIamPFPKyAqWi10iWuvKlQn/Yao=
github.com/werf/copy-recurse v0.2.4 h1:kEyGUKhgS8WdEOjInNQKgk4lqPWzP2AgR27F3dcGsVc=
github.com/werf/copy-recurse v0.2.4/go.mod h1:KVHSQ90p19xflWW0B7BJhLBwmSbEtuxIaBnjlUYRPhk=
github.com/werf/helm v0.0.0-20210202111118-81e74d46da0f h1:81YscYTF9mmTf0ULOsCmm42YWQp+qWDzWi1HjWniZrg=
Expand Down
3 changes: 3 additions & 0 deletions pkg/deploy/helm/annotations.go
Expand Up @@ -21,4 +21,7 @@ const (
ReplicasOnCreationAnnoName = "werf.io/replicas-on-creation"

StageWeightAnnoName = "werf.io/weight"

ExternalDependencyResourceAnnoName = "external-dependency.werf.io/resource"
ExternalDependencyNamespaceAnnoName = "external-dependency.werf.io/namespace"
)
160 changes: 160 additions & 0 deletions pkg/deploy/helm/external_deps_annotations_parser.go
@@ -0,0 +1,160 @@
package helm

import (
"fmt"
"strings"

"github.com/werf/werf/pkg/slug"
"helm.sh/helm/v3/pkg/phases/stages/externaldeps"
)

func NewExternalDepsAnnotationsParser() *ExternalDepsAnnotationsParser {
return &ExternalDepsAnnotationsParser{}
}

type ExternalDepsAnnotationsParser struct{}

func (s *ExternalDepsAnnotationsParser) Parse(annotations map[string]string) (externaldeps.ExternalDependencyList, error) {
extDeps, err := s.parseResourceAnnotations(annotations)
if err != nil {
return nil, fmt.Errorf("error parsing ext deps resource annotations: %w", err)
}

extDeps, err = s.parseNamespaceAnnotations(extDeps, annotations)
if err != nil {
return nil, fmt.Errorf("error parsing ext deps namespace annotations: %w", err)
}

return extDeps, nil
}

func (s *ExternalDepsAnnotationsParser) parseResourceAnnotations(annotations map[string]string) (externaldeps.ExternalDependencyList, error) {
var externalDependencyList externaldeps.ExternalDependencyList
for annoKey, annoVal := range annotations {
annoKey, annoVal = s.normalizeAnnotation(annoKey, annoVal)

if !s.matchResourceAnnotation(annoKey) {
continue
}

if err := s.validateResourceAnnotation(annoKey, annoVal); err != nil {
return nil, fmt.Errorf("error validating external dependency resource annotation: %w", err)
}

name := s.parseResourceAnnotationKey(annoKey)
resourceType, resourceName := s.parseResourceAnnotationValue(annoVal)

externalDependencyList = append(externalDependencyList, externaldeps.NewExternalDependency(name, resourceType, resourceName))
}

return externalDependencyList, nil
}

func (s *ExternalDepsAnnotationsParser) parseNamespaceAnnotations(extDeps externaldeps.ExternalDependencyList, annotations map[string]string) (externaldeps.ExternalDependencyList, error) {
for annoKey, annoVal := range annotations {
annoKey, annoVal = s.normalizeAnnotation(annoKey, annoVal)

if !s.matchNamespaceAnnotation(annoKey) {
continue
}

if err := s.validateNamespaceAnnotation(annoKey, annoVal); err != nil {
return nil, fmt.Errorf("error validating external dependency namespace annotation: %w", err)
}

name := s.parseNamespaceAnnotationKey(annoKey)

for _, extDep := range extDeps {
if extDep.Name == name {
extDep.Namespace = annoVal
break
}
}
}

return extDeps, nil
}

func (s *ExternalDepsAnnotationsParser) normalizeAnnotation(key, value string) (string, string) {
key = strings.TrimSpace(key)
key = strings.Trim(key, "/.")
key = strings.TrimSpace(key)

value = strings.TrimSpace(value)

return key, value
}

func (s *ExternalDepsAnnotationsParser) matchResourceAnnotation(key string) bool {
return strings.HasSuffix(key, ExternalDependencyResourceAnnoName)
}

func (s *ExternalDepsAnnotationsParser) matchNamespaceAnnotation(key string) bool {
return strings.HasSuffix(key, ExternalDependencyNamespaceAnnoName)
}

func (s *ExternalDepsAnnotationsParser) validateResourceAnnotation(key, value string) error {
if key == ExternalDependencyResourceAnnoName {
return fmt.Errorf("annotation %q should have prefix specified, e.g. \"backend.%s\"", key, ExternalDependencyResourceAnnoName)
}

if value == "" {
return fmt.Errorf("annotation %q value should be specified", key)
}

valueElems := strings.Split(value, "/")

if len(valueElems) != 2 {
return fmt.Errorf("wrong annotation %q value format, should be: type/name", key)
}

switch valueElems[0] {
case "":
return fmt.Errorf("in annotation %q resource type can't be empty", key)
case "all":
return fmt.Errorf("\"all\" resource type is not allowed in annotation %q", key)
}

resourceTypeParts := strings.Split(valueElems[0], ".")
for _, part := range resourceTypeParts {
if part == "" {
return fmt.Errorf("resource type in annotation %q should have dots (.) delimiting only non-empty resource.version.group: %s", ExternalDependencyResourceAnnoName, key)
}
}

switch valueElems[1] {
case "":
return fmt.Errorf("in annotation %q resource name can't be empty", key)
}

return nil
}

func (s *ExternalDepsAnnotationsParser) validateNamespaceAnnotation(key, value string) error {
if key == ExternalDependencyNamespaceAnnoName {
return fmt.Errorf("annotation %q should have prefix specified, e.g. \"backend.%s\"", key, ExternalDependencyNamespaceAnnoName)
}

if value == "" {
return fmt.Errorf("annotation %q value should be specified", key)
}

if err := slug.ValidateKubernetesNamespace(value); err != nil {
return fmt.Errorf("error validating annotation \"%s=%s\" namespace name: %w", key, value, err)
}

return nil
}

func (s *ExternalDepsAnnotationsParser) parseResourceAnnotationKey(key string) (name string) {
return strings.TrimSuffix(key, fmt.Sprint(".", ExternalDependencyResourceAnnoName))
}

func (s *ExternalDepsAnnotationsParser) parseResourceAnnotationValue(value string) (resourceType, resourceName string) {
elems := strings.Split(value, "/")
return elems[0], elems[1]
}

func (s *ExternalDepsAnnotationsParser) parseNamespaceAnnotationKey(key string) (name string) {
return strings.TrimSuffix(key, fmt.Sprint(".", ExternalDependencyNamespaceAnnoName))
}

0 comments on commit 1c607aa

Please sign in to comment.