Skip to content

Commit

Permalink
refactoring deployments tests
Browse files Browse the repository at this point in the history
  • Loading branch information
stefbenoist committed Nov 21, 2019
1 parent d44010e commit 5827a68
Show file tree
Hide file tree
Showing 20 changed files with 328 additions and 241 deletions.
84 changes: 46 additions & 38 deletions deployments/artifacts.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,82 +20,90 @@ import (
"path"

"github.com/pkg/errors"

"github.com/ystia/yorc/v4/helper/consulutil"
)

// GetArtifactsForType returns a map of artifact name / artifact file for the given type.
func updateArtifactsForType(ctx context.Context, deploymentID, typeName, tType, importPath string, artifacts map[string]string) error {
var typ interface{}
switch tType {
case "node":
typ = new(tosca.NodeType)
case "relationship":
typ = new(tosca.RelationshipType)
default:
return errors.Errorf("the type:%q with name:%q is not expected to have artifacts", tType, typeName)
}

err := getTypeStruct(deploymentID, typeName, typ)
if err != nil {
return err
}
var artifactsMap tosca.ArtifactDefMap
switch t := typ.(type) {
case *tosca.NodeType:
artifactsMap = t.Artifacts
case *tosca.RelationshipType:
artifactsMap = t.Artifacts
}

for k, v := range artifactsMap {
if v.File != "" {
// TODO path is relative to the type and may not be the same as a child type
artifacts[k] = path.Join(importPath, v.File)
}
}

return nil
}

// GetFileArtifactsForType returns a map of artifact name / artifact file for the given type of type tType
//
// The returned artifacts paths are relative to root of the deployment archive.
// It traverse the 'derived_from' relations to support inheritance of artifacts. Parent artifacts are fetched first and may be overridden by child types
func GetArtifactsForType(ctx context.Context, deploymentID, typeName string) (map[string]string, error) {
func GetFileArtifactsForType(ctx context.Context, deploymentID, typeName, tType string) (map[string]string, error) {
parentType, err := GetParentType(ctx, deploymentID, typeName)
if err != nil {
return nil, err
}
var artifacts map[string]string
if parentType != "" {
artifacts, err = GetArtifactsForType(ctx, deploymentID, parentType)
artifacts, err = GetFileArtifactsForType(ctx, deploymentID, parentType, tType)
if err != nil {
return nil, err
}
} else {
artifacts = make(map[string]string)
}
typePath, err := locateTypePath(deploymentID, typeName)
if err != nil {
return nil, err
}

artifactsPath := path.Join(typePath, "artifacts")
importPath, err := GetTypeImportPath(ctx, deploymentID, typeName)
if err != nil {
return nil, err
}
err = updateArtifactsFromPath(artifacts, artifactsPath, importPath)
err = updateArtifactsForType(ctx, deploymentID, typeName, tType, importPath, artifacts)
return artifacts, errors.Wrapf(err, "Failed to get artifacts for type: %q", typeName)
}

// GetArtifactsForNode returns a map of artifact name / artifact file for the given node.
// GetFileArtifactsForNode returns a map of artifact name / artifact file for the given node.
//
// The returned artifacts paths are relative to root of the deployment archive.
// It will first fetch artifacts from it node type and its parents and fetch artifacts for the node template itself.
// This way artifacts from a parent type may be overridden by child types and artifacts from node type may be overridden by the node template
func GetArtifactsForNode(ctx context.Context, deploymentID, nodeName string) (map[string]string, error) {
nodeType, err := GetNodeType(ctx, deploymentID, nodeName)
func GetFileArtifactsForNode(ctx context.Context, deploymentID, nodeName string) (map[string]string, error) {
node, err := getNodeTemplateStruct(ctx, deploymentID, nodeName)
if err != nil {
return nil, err
}
artifacts, err := GetArtifactsForType(ctx, deploymentID, nodeType)
artifacts, err := GetFileArtifactsForType(ctx, deploymentID, node.Type, "node")
if err != nil {
return nil, err
}
artifactsPath := path.Join(consulutil.DeploymentKVPrefix, deploymentID, "topology/nodes", nodeName, "artifacts")
// No importPath for node templates as they will be CSAR root relative
err = updateArtifactsFromPath(artifacts, artifactsPath, "")
return artifacts, errors.Wrapf(err, "Failed to get artifacts for node: %q", nodeName)
}

// updateArtifactsFromPath returns a map of artifact name / artifact file for the given node or type denoted by the given artifactsPath.
func updateArtifactsFromPath(artifacts map[string]string, artifactsPath, importPath string) error {
keys, err := consulutil.GetKeys(artifactsPath)
if err != nil {
return errors.Wrap(err, consulutil.ConsulGenericErrMsg)
}

for _, artifactPath := range keys {
artifactName := path.Base(artifactPath)
exist, value, err := consulutil.GetStringValue(path.Join(artifactPath, "file"))
if err != nil {
return errors.Wrap(err, consulutil.ConsulGenericErrMsg)
}
if !exist || value == "" {
return errors.Errorf("Missing mandatory attribute \"file\" for artifact %q", path.Base(artifactPath))
// No importPath for node templates as they will be CSAR root relative
for k, v := range node.Artifacts {
if v.File != "" {
artifacts[k] = v.File
}
// TODO path is relative to the type and may not be the same as a child type
artifacts[artifactName] = path.Join(importPath, value)
}
return nil
return artifacts, errors.Wrapf(err, "Failed to get artifacts for node: %q", nodeName)
}

// GetArtifactTypeExtensions returns the extensions defined in this artifact type.
Expand Down
24 changes: 16 additions & 8 deletions deployments/artifacts_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ package deployments

import (
"context"
"github.com/ystia/yorc/v4/storage"
"github.com/ystia/yorc/v4/storage/types"
"github.com/ystia/yorc/v4/tosca"
"strings"
"testing"

Expand All @@ -33,9 +36,14 @@ func testArtifacts(t *testing.T, srv1 *testutil.TestServer) {
err := StoreDeploymentDefinition(context.Background(), deploymentID, "testdata/artifacts.yaml")
require.Nil(t, err)

srv1.PopulateKV(t, map[string][]byte{
consulutil.DeploymentKVPrefix + "/" + deploymentID + "/topology/types/yorc.types.A/importPath": []byte("path/to/typeA"),
})
// Update the type with importPath (not in Tosca specifications)
typeName := "yorc.types.A"
typToUpdate := new(tosca.NodeType)
err = getTypeStruct(deploymentID, typeName, typToUpdate)
require.Nil(t, err)
typToUpdate.ImportPath = "path/to/typeA"
err = storage.GetStore(types.StoreTypeDeployment).Set(consulutil.DeploymentKVPrefix+"/"+deploymentID+"/topology/types/yorc.types.A", typToUpdate)
require.Nil(t, err)

t.Run("groupDeploymentArtifacts", func(t *testing.T) {
t.Run("TestGetArtifactsForType", func(t *testing.T) {
Expand All @@ -48,7 +56,7 @@ func testArtifacts(t *testing.T, srv1 *testutil.TestServer) {
}

func testGetArtifactsForType(t *testing.T, deploymentID string) {
artifacts, err := GetArtifactsForType(context.Background(), deploymentID, "yorc.types.A")
artifacts, err := GetFileArtifactsForType(context.Background(), deploymentID, "yorc.types.A", "node")
require.Nil(t, err)
require.NotNil(t, artifacts)
require.Len(t, artifacts, 5)
Expand All @@ -63,7 +71,7 @@ func testGetArtifactsForType(t *testing.T, deploymentID string) {
require.Contains(t, artifacts, "art5")
require.Equal(t, "ParentA", artifacts["art5"])

artifacts, err = GetArtifactsForType(context.Background(), deploymentID, "yorc.types.ParentA")
artifacts, err = GetFileArtifactsForType(context.Background(), deploymentID, "yorc.types.ParentA", "node")
require.Nil(t, err)
require.NotNil(t, artifacts)
require.Len(t, artifacts, 3)
Expand All @@ -74,14 +82,14 @@ func testGetArtifactsForType(t *testing.T, deploymentID string) {
require.Contains(t, artifacts, "art5")
require.Equal(t, "ParentA", artifacts["art5"])

artifacts, err = GetArtifactsForType(context.Background(), deploymentID, "root")
artifacts, err = GetFileArtifactsForType(context.Background(), deploymentID, "root", "node")
require.Nil(t, err)
require.NotNil(t, artifacts)
require.Len(t, artifacts, 0)

}
func testGetArtifactsForNode(t *testing.T, deploymentID string) {
artifacts, err := GetArtifactsForNode(context.Background(), deploymentID, "NodeA")
artifacts, err := GetFileArtifactsForNode(context.Background(), deploymentID, "NodeA")
require.Nil(t, err)
require.NotNil(t, artifacts)
require.Len(t, artifacts, 6)
Expand All @@ -99,7 +107,7 @@ func testGetArtifactsForNode(t *testing.T, deploymentID string) {
require.Contains(t, artifacts, "art6")
require.Equal(t, "path/to/typeA/TypeA", artifacts["art6"])

artifacts, err = GetArtifactsForNode(context.Background(), deploymentID, "NodeB")
artifacts, err = GetFileArtifactsForNode(context.Background(), deploymentID, "NodeB")
require.Nil(t, err)
require.NotNil(t, artifacts)
require.Len(t, artifacts, 0)
Expand Down
58 changes: 48 additions & 10 deletions deployments/capabilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ func GetCapabilitiesOfType(ctx context.Context, deploymentID, typeName, capabili
// It returns false if there is no such property
func GetCapabilityPropertyType(ctx context.Context, deploymentID, nodeName, capabilityName, propertyName string) (bool, string, error) {
capType, err := GetNodeCapabilityType(ctx, deploymentID, nodeName, capabilityName)
if err != nil {
if err != nil || capType == "" {
return false, "", err
}

propDef, err := getCapabilityPropertyDefinition(ctx, deploymentID, capType, propertyName)
return propDef == nil, propDef.Type, err
if err != nil || propDef == nil {
return false, "", err
}
return true, propDef.Type, err
}

func getCapabilityPropertyDefinition(ctx context.Context, deploymentID, capabilityTypeName, propertyName string) (*tosca.PropertyDefinition, error) {
Expand Down Expand Up @@ -155,13 +157,14 @@ func GetCapabilityPropertyValue(ctx context.Context, deploymentID, nodeName, cap
if err != nil {
return nil, err
}

propDef, err := getCapabilityPropertyDefinition(ctx, deploymentID, capabilityType, propertyName)
if err != nil {
return nil, err
}
if propDef != nil {
return getValueAssignment(ctx, deploymentID, nodeName, "", "", va, propDef.Default, nestedKeys...)
if capabilityType != "" {
propDef, err := getCapabilityPropertyDefinition(ctx, deploymentID, capabilityType, propertyName)
if err != nil {
return nil, err
}
if propDef != nil {
return getValueAssignment(ctx, deploymentID, nodeName, "", "", va, propDef.Default, nestedKeys...)
}
}

// No default found in type hierarchy
Expand All @@ -176,6 +179,41 @@ func GetCapabilityPropertyValue(ctx context.Context, deploymentID, nodeName, cap
return value, err
}
}

hasProp, propDataType, err := GetCapabilityPropertyType(ctx, deploymentID, nodeName, capabilityName, propertyName)
if err != nil {
return nil, err
}
if hasProp && capabilityType != "" {
// Check if the whole property is optional
isRequired, err := IsTypePropertyRequired(ctx, deploymentID, capabilityType, "capability", propertyName)
if err != nil {
return nil, err
}
if !isRequired {
// For backward compatibility
// TODO this doesn't look as a good idea to me
return &TOSCAValue{Value: ""}, nil
}

if len(nestedKeys) > 1 && propDataType != "" {
// Check if nested type is optional
nestedKeyType, err := GetNestedDataType(ctx, deploymentID, propDataType, nestedKeys[:len(nestedKeys)-1]...)
if err != nil {
return nil, err
}
isRequired, err = IsTypePropertyRequired(ctx, deploymentID, nestedKeyType, "data", nestedKeys[len(nestedKeys)-1])
if err != nil {
return nil, err
}
if !isRequired {
// For backward compatibility
// TODO this doesn't look as a good idea to me
return &TOSCAValue{Value: ""}, nil
}
}
}

// Not found anywhere
return nil, nil
}
Expand Down
64 changes: 36 additions & 28 deletions deployments/capabilities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ package deployments

import (
"context"
"github.com/ystia/yorc/v4/storage"
"github.com/ystia/yorc/v4/storage/types"
"github.com/ystia/yorc/v4/tosca"
"reflect"
"strings"
"testing"
Expand All @@ -34,37 +37,42 @@ func testCapabilities(t *testing.T, srv1 *testutil.TestServer) {
require.Nil(t, err)

srv1.PopulateKV(t, map[string][]byte{
// overwrite type to an non-existing one now we have passed StoreDeploymentDefinition verifications
consulutil.DeploymentKVPrefix + "/" + deploymentID + "/topology/types/yorc.type.WithUndefCap/capabilities/udef/type": []byte("yorc.capabilities.Undefined"),

consulutil.DeploymentKVPrefix + "/" + deploymentID + "/topology/instances/node1/0/capabilities/endpoint/attributes/ip_address": []byte("0.0.0.0"),
consulutil.DeploymentKVPrefix + "/" + deploymentID + "/topology/instances/node1/0/capabilities/endpoint/attributes/credentials/user": []byte("ubuntu"),
consulutil.DeploymentKVPrefix + "/" + deploymentID + "/topology/instances/Compute/0/attributes/private_address": []byte("10.0.0.1"),
consulutil.DeploymentKVPrefix + "/" + deploymentID + "/topology/instances/Compute/0/attributes/public_address": []byte("10.1.0.1"),
})

// overwrite type to an non-existing one now we have passed StoreDeploymentDefinition verifications
typ := tosca.NodeType{}
typ.Capabilities = map[string]tosca.CapabilityDefinition{
"undef": {Type: "yorc.capabilities.Undefined"},
}
err = storage.GetStore(types.StoreTypeDeployment).Set(consulutil.DeploymentKVPrefix+"/"+deploymentID+"/topology/types/yorc.type.WithUndefCap", typ)
require.Nil(t, err)

t.Run("groupDeploymentsCapabilities", func(t *testing.T) {
//t.Run("TestHasScalableCapability", func(t *testing.T) {
// testHasScalableCapability(t, deploymentID)
//})
//t.Run("TestGetCapabilitiesOfType", func(t *testing.T) {
// testGetCapabilitiesOfType(t, deploymentID)
//})
//t.Run("TestGetNodeCapabilityType", func(t *testing.T) {
// testGetNodeCapabilityType(t, deploymentID)
//})
//t.Run("TestGetCapabilityProperty", func(t *testing.T) {
// testGetCapabilityProperty(t, deploymentID)
//})
//t.Run("TestGetCapabilityPropertyType", func(t *testing.T) {
// testGetCapabilityPropertyType(t, deploymentID)
//})
t.Run("TestHasScalableCapability", func(t *testing.T) {
testHasScalableCapability(t, deploymentID)
})
t.Run("TestGetCapabilitiesOfType", func(t *testing.T) {
testGetCapabilitiesOfType(t, deploymentID)
})
t.Run("TestGetNodeCapabilityType", func(t *testing.T) {
testGetNodeCapabilityType(t, deploymentID)
})
t.Run("TestGetCapabilityProperty", func(t *testing.T) {
testGetCapabilityProperty(t, deploymentID)
})
t.Run("TestGetCapabilityPropertyType", func(t *testing.T) {
testGetCapabilityPropertyType(t, deploymentID)
})
t.Run("TestGetInstanceCapabilityAttribute", func(t *testing.T) {
testGetInstanceCapabilityAttribute(t, deploymentID)
})
//t.Run("TestGetIPAddressFromHost", func(t *testing.T) {
// testGetIPAddressFromHost(t, deploymentID)
//})
t.Run("TestGetIPAddressFromHost", func(t *testing.T) {
testGetIPAddressFromHost(t, deploymentID)
})
})
}

Expand Down Expand Up @@ -146,8 +154,8 @@ func testGetCapabilityProperty(t *testing.T, deploymentID string) {
{"GetCapabilityPropertyDefinedInNode", args{"node1", "scalable", "min_instances"}, true, "10", false},
{"GetCapabilityPropertyDefinedInNodeOfInheritedType", args{"node1", "scalable", "default_instances"}, true, "1", false},
{"GetCapabilityPropertyDefinedAsCapTypeDefault", args{"node1", "scalable", "max_instances"}, true, "100", false},
{"GetCapabilityPropertyUndefinedProp", args{"node1", "scalable", "udef"}, false, "", false},
{"GetCapabilityPropertyUndefinedCap", args{"node1", "udef", "udef"}, false, "", false},
{"GetCapabilityPropertyUndefinedProp", args{"node1", "scalable", "undef"}, false, "", false},
{"GetCapabilityPropertyUndefinedCap", args{"node1", "undef", "undef"}, false, "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -188,9 +196,9 @@ func testGetCapabilityPropertyType(t *testing.T, deploymentID string) {
{"GetCapabilityPropertyTypeStringDefinedInNodeOfInheritedType",
args{"node1", "endpoint", "prop1"}, true, "string", false},
{"GetCapabilityPropertyUndefinedProp",
args{"node1", "scalable", "udef"}, false, "", false},
args{"node1", "scalable", "undef"}, false, "", false},
{"GetCapabilityPropertyUndefinedCap",
args{"node1", "udef", "udef"}, false, "", false},
args{"node1", "undef", "undef"}, false, "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down Expand Up @@ -239,8 +247,8 @@ func testGetInstanceCapabilityAttribute(t *testing.T, deploymentID string) {
{"GetCapabilityAttributeFromPropertyDefinedInNode", args{"node1", "0", "scalable", "min_instances", nil}, true, "10", false},
{"GetCapabilityAttributeFromPropertyDefinedInNodeOfInheritedType", args{"node1", "0", "scalable", "default_instances", nil}, true, "1", false},
{"GetCapabilityAttributeFromPropertyDefinedAsCapTypeDefault", args{"node1", "0", "scalable", "max_instances", nil}, true, "100", false},
{"GetCapabilityAttributeFromPropertyUndefinedProp", args{"node1", "0", "scalable", "udef", nil}, false, "", false},
{"GetCapabilityAttributeFromPropertyUndefinedCap", args{"node1", "0", "udef", "udef", nil}, false, "", false},
{"GetCapabilityAttributeFromPropertyUndefinedProp", args{"node1", "0", "scalable", "undef", nil}, false, "", false},
{"GetCapabilityAttributeFromPropertyUndefinedCap", args{"node1", "0", "undef", "undef", nil}, false, "", false},

// Test cases for properties as attributes mapping
{"GetCapabilityAttributeKeyFromPropertyDefinedInInstance", args{"node1", "0", "endpoint", "credentials", []string{"user"}}, true, "ubuntu", false},
Expand Down Expand Up @@ -279,7 +287,7 @@ func testGetNodeCapabilityType(t *testing.T, deploymentID string) {
{"CapNotFoundOnNode3", args{"node3", "scalable"}, "", false},
{"GetEndpointCapTypeOnNode1", args{"node1", "endpoint"}, "yorc.test.capabilities.Endpoint", false},
{"GetEndpointCapTypeOnNode2", args{"node2", "binding"}, "yorc.test.capabilities.network.Bindable", false},
{"UndefCapOnNodeWithUndefCap", args{"NodeWithUndefCap", "udef"}, "yorc.capabilities.Undefined", false},
{"UndefCapOnNodeWithUndefCap", args{"NodeWithUndefCap", "undef"}, "yorc.capabilities.Undefined", false},
{"CapWithInheritance", args{"SuperScalableNode", "sups"}, "yorc.capabilities.SuperScalable", false},
}
for _, tt := range tests {
Expand Down
Loading

0 comments on commit 5827a68

Please sign in to comment.