Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(external-deps): external dependencies for release resources
Added a way to express external dependencies for the new release resource, so that we will wait until the external dependency resource is ready and only after that we will try to deploy the new release resource. Example usage: ```yaml metadata: annotations: app1.external-dependency.werf.io/resource: "deployment/app1" app1.external-dependency.werf.io/namespace: "default" # optional ``` Signed-off-by: Ilya Lesikov <ilya@lesikov.com>
- Loading branch information
1 parent
a64d682
commit 73e6bcc
Showing
17 changed files
with
384 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)) | ||
} |
Oops, something went wrong.