-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Refactor template lookup to use strategy pattern (#2200)
* feat: Refactor template lookup to use strategy pattern * fix deletion test * fix linting * slim interface signature * add coverage entry * adapt expected coverage * fix e2e tests * fix deletion test * add comments to new types * rename strategies * add test cases for strategies --------- Co-authored-by: Benjamin Lindner <[email protected]>
- Loading branch information
Showing
28 changed files
with
950 additions
and
451 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
135 changes: 135 additions & 0 deletions
135
pkg/templatelookup/moduletemplateinfolookup/by_channel_strategy.go
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,135 @@ | ||
package moduletemplateinfolookup | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"fmt" | ||
|
||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
logf "sigs.k8s.io/controller-runtime/pkg/log" | ||
|
||
"github.com/kyma-project/lifecycle-manager/api/v1beta2" | ||
"github.com/kyma-project/lifecycle-manager/pkg/log" | ||
"github.com/kyma-project/lifecycle-manager/pkg/templatelookup" | ||
) | ||
|
||
var ErrNotDefaultChannelAllowed = errors.New("specifying no default channel is not allowed") | ||
|
||
// ByChannelStrategy looks up the module template for a given channel-based installation. | ||
type ByChannelStrategy struct { | ||
client client.Reader | ||
} | ||
|
||
func NewByChannelStrategy(client client.Reader) ByChannelStrategy { | ||
return ByChannelStrategy{client: client} | ||
} | ||
|
||
func (ByChannelStrategy) IsResponsible(moduleInfo *templatelookup.ModuleInfo, moduleReleaseMeta *v1beta2.ModuleReleaseMeta) bool { | ||
if moduleReleaseMeta != nil { | ||
return false | ||
} | ||
|
||
if moduleInfo.IsInstalledByVersion() { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
func (s ByChannelStrategy) Lookup(ctx context.Context, | ||
moduleInfo *templatelookup.ModuleInfo, | ||
kyma *v1beta2.Kyma, | ||
_ *v1beta2.ModuleReleaseMeta, | ||
) templatelookup.ModuleTemplateInfo { | ||
desiredChannel := getDesiredChannel(moduleInfo.Channel, kyma.Spec.Channel) | ||
info := templatelookup.ModuleTemplateInfo{ | ||
DesiredChannel: desiredChannel, | ||
} | ||
|
||
template, err := s.filterTemplatesByChannel(ctx, moduleInfo.Name, desiredChannel) | ||
if err != nil { | ||
info.Err = err | ||
return info | ||
} | ||
|
||
actualChannel := template.Spec.Channel | ||
if actualChannel == "" { | ||
info.Err = fmt.Errorf( | ||
"no channel found on template for module: %s: %w", | ||
moduleInfo.Name, ErrNotDefaultChannelAllowed, | ||
) | ||
return info | ||
} | ||
|
||
logUsedChannel(ctx, moduleInfo.Name, actualChannel, kyma.Spec.Channel) | ||
info.ModuleTemplate = template | ||
return info | ||
} | ||
|
||
func (s ByChannelStrategy) filterTemplatesByChannel(ctx context.Context, name, desiredChannel string) ( | ||
*v1beta2.ModuleTemplate, error, | ||
) { | ||
templateList := &v1beta2.ModuleTemplateList{} | ||
err := s.client.List(ctx, templateList) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to list module templates on lookup: %w", err) | ||
} | ||
|
||
var filteredTemplates []*v1beta2.ModuleTemplate | ||
for _, template := range templateList.Items { | ||
if TemplateNameMatch(&template, name) && template.Spec.Channel == desiredChannel { | ||
filteredTemplates = append(filteredTemplates, &template) | ||
continue | ||
} | ||
} | ||
|
||
if len(filteredTemplates) > 1 { | ||
return nil, newMoreThanOneTemplateCandidateErr(name, templateList.Items) | ||
} | ||
|
||
if len(filteredTemplates) == 0 { | ||
return nil, fmt.Errorf("%w: for module %s in channel %s ", | ||
ErrNoTemplatesInListResult, name, desiredChannel) | ||
} | ||
|
||
if filteredTemplates[0].Spec.Mandatory { | ||
return nil, fmt.Errorf("%w: for module %s in channel %s", | ||
ErrTemplateMarkedAsMandatory, name, desiredChannel) | ||
} | ||
|
||
return filteredTemplates[0], nil | ||
} | ||
|
||
func getDesiredChannel(moduleChannel, globalChannel string) string { | ||
var desiredChannel string | ||
|
||
switch { | ||
case moduleChannel != "": | ||
desiredChannel = moduleChannel | ||
case globalChannel != "": | ||
desiredChannel = globalChannel | ||
default: | ||
desiredChannel = v1beta2.DefaultChannel | ||
} | ||
|
||
return desiredChannel | ||
} | ||
|
||
func logUsedChannel(ctx context.Context, name string, actualChannel string, defaultChannel string) { | ||
logger := logf.FromContext(ctx) | ||
if actualChannel != defaultChannel { | ||
logger.V(log.DebugLevel).Info( | ||
fmt.Sprintf( | ||
"using %s (instead of %s) for module %s", | ||
actualChannel, defaultChannel, name, | ||
), | ||
) | ||
} else { | ||
logger.V(log.DebugLevel).Info( | ||
fmt.Sprintf( | ||
"using %s for module %s", | ||
actualChannel, name, | ||
), | ||
) | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
pkg/templatelookup/moduletemplateinfolookup/by_channel_strategy_test.go
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,69 @@ | ||
package moduletemplateinfolookup_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
|
||
"github.com/kyma-project/lifecycle-manager/api/v1beta2" | ||
"github.com/kyma-project/lifecycle-manager/pkg/templatelookup/moduletemplateinfolookup" | ||
"github.com/kyma-project/lifecycle-manager/pkg/testutils/builder" | ||
) | ||
|
||
func Test_ByChannelStrategy_IsResponsible_ReturnsTrue(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithChannel("regular").Enabled().Build() | ||
var moduleReleaseMeta *v1beta2.ModuleReleaseMeta = nil | ||
byChannelStrategy := moduletemplateinfolookup.NewByChannelStrategy(nil) | ||
|
||
responsible := byChannelStrategy.IsResponsible(moduleInfo, moduleReleaseMeta) | ||
|
||
assert.True(t, responsible) | ||
} | ||
|
||
func Test_ByChannelStrategy_IsResponsible_ReturnsFalse_WhenModuleReleaseMetaIsNotNil(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithChannel("regular").Enabled().Build() | ||
moduleReleaseMeta := builder.NewModuleReleaseMetaBuilder().Build() | ||
byChannelStrategy := moduletemplateinfolookup.NewByChannelStrategy(nil) | ||
|
||
responsible := byChannelStrategy.IsResponsible(moduleInfo, moduleReleaseMeta) | ||
|
||
assert.False(t, responsible) | ||
} | ||
|
||
func Test_ByChannelStrategy_IsResponsible_ReturnsFalse_WhenInstalledByVersion(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithVersion("1.0.0").Enabled().Build() | ||
var moduleReleaseMeta *v1beta2.ModuleReleaseMeta = nil | ||
byChannelStrategy := moduletemplateinfolookup.NewByChannelStrategy(nil) | ||
|
||
responsible := byChannelStrategy.IsResponsible(moduleInfo, moduleReleaseMeta) | ||
|
||
assert.False(t, responsible) | ||
} | ||
|
||
func Test_ByChannelStrategy_Lookup_ReturnsModuleTemplateInfo(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithName("test-module").WithChannel("regular").Enabled().Build() | ||
kyma := builder.NewKymaBuilder().Build() | ||
var moduleReleaseMeta *v1beta2.ModuleReleaseMeta = nil | ||
moduleTemplate := builder.NewModuleTemplateBuilder(). | ||
WithName("test-module-regular"). | ||
WithModuleName("test-module"). | ||
WithVersion(""). | ||
WithChannel("regular"). | ||
Build() | ||
byChannelStrategy := moduletemplateinfolookup.NewByChannelStrategy(fakeClient( | ||
&v1beta2.ModuleTemplateList{ | ||
Items: []v1beta2.ModuleTemplate{ | ||
*moduleTemplate, | ||
}, | ||
}, | ||
)) | ||
|
||
moduleTemplateInfo := byChannelStrategy.Lookup(context.Background(), moduleInfo, kyma, moduleReleaseMeta) | ||
|
||
assert.NotNil(t, moduleTemplateInfo) | ||
assert.Equal(t, moduleTemplate.Name, moduleTemplateInfo.ModuleTemplate.Name) | ||
assert.Equal(t, moduleTemplate.Spec.ModuleName, moduleTemplateInfo.ModuleTemplate.Spec.ModuleName) | ||
assert.Equal(t, moduleTemplate.Spec.Version, moduleTemplateInfo.ModuleTemplate.Spec.Version) | ||
assert.Equal(t, moduleTemplate.Spec.Channel, moduleTemplateInfo.ModuleTemplate.Spec.Channel) | ||
} |
52 changes: 52 additions & 0 deletions
52
pkg/templatelookup/moduletemplateinfolookup/by_module_release_meta_strategy.go
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,52 @@ | ||
package moduletemplateinfolookup | ||
|
||
import ( | ||
"context" | ||
|
||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
"github.com/kyma-project/lifecycle-manager/api/v1beta2" | ||
"github.com/kyma-project/lifecycle-manager/pkg/templatelookup" | ||
) | ||
|
||
// ByModuleReleaseMetaStrategy looks up the module template via the module release meta. | ||
// It only supports channel-based installation. | ||
type ByModuleReleaseMetaStrategy struct { | ||
client client.Reader | ||
} | ||
|
||
func NewByModuleReleaseMetaStrategy(client client.Reader) ByModuleReleaseMetaStrategy { | ||
return ByModuleReleaseMetaStrategy{client: client} | ||
} | ||
|
||
func (ByModuleReleaseMetaStrategy) IsResponsible(_ *templatelookup.ModuleInfo, moduleReleaseMeta *v1beta2.ModuleReleaseMeta) bool { | ||
return moduleReleaseMeta != nil | ||
} | ||
|
||
func (s ByModuleReleaseMetaStrategy) Lookup(ctx context.Context, | ||
moduleInfo *templatelookup.ModuleInfo, | ||
kyma *v1beta2.Kyma, | ||
moduleReleaseMeta *v1beta2.ModuleReleaseMeta, | ||
) templatelookup.ModuleTemplateInfo { | ||
moduleTemplateInfo := templatelookup.ModuleTemplateInfo{} | ||
|
||
moduleTemplateInfo.DesiredChannel = getDesiredChannel(moduleInfo.Channel, kyma.Spec.Channel) | ||
desiredModuleVersion, err := templatelookup.GetChannelVersionForModule(moduleReleaseMeta, moduleTemplateInfo.DesiredChannel) | ||
if err != nil { | ||
moduleTemplateInfo.Err = err | ||
return moduleTemplateInfo | ||
} | ||
|
||
template, err := getTemplateByVersion(ctx, | ||
s.client, | ||
moduleInfo.Name, | ||
desiredModuleVersion, | ||
kyma.Namespace) | ||
if err != nil { | ||
moduleTemplateInfo.Err = err | ||
return moduleTemplateInfo | ||
} | ||
|
||
moduleTemplateInfo.ModuleTemplate = template | ||
return moduleTemplateInfo | ||
} |
80 changes: 80 additions & 0 deletions
80
pkg/templatelookup/moduletemplateinfolookup/by_module_release_meta_strategy_test.go
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,80 @@ | ||
package moduletemplateinfolookup_test | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
machineryruntime "k8s.io/apimachinery/pkg/runtime" | ||
machineryutilruntime "k8s.io/apimachinery/pkg/util/runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/client/fake" | ||
|
||
"github.com/kyma-project/lifecycle-manager/api" | ||
"github.com/kyma-project/lifecycle-manager/api/v1beta2" | ||
"github.com/kyma-project/lifecycle-manager/pkg/templatelookup/moduletemplateinfolookup" | ||
"github.com/kyma-project/lifecycle-manager/pkg/testutils/builder" | ||
) | ||
|
||
func Test_ByModuleReleaseMetaStrategy_IsResponsible_ReturnsTrue(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithChannel("regular").Enabled().Build() | ||
moduleReleaseMeta := builder.NewModuleReleaseMetaBuilder().Build() | ||
byMRMStrategy := moduletemplateinfolookup.NewByModuleReleaseMetaStrategy(nil) | ||
|
||
responsible := byMRMStrategy.IsResponsible(moduleInfo, moduleReleaseMeta) | ||
|
||
assert.True(t, responsible) | ||
} | ||
|
||
func Test_ByModuleReleaseMetaStrategy_IsResponsible_ReturnsFalse_WhenModuleReleaseMetaIsNotNil(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithVersion("regular").Enabled().Build() | ||
var moduleReleaseMeta *v1beta2.ModuleReleaseMeta = nil | ||
byMRMStrategy := moduletemplateinfolookup.NewByModuleReleaseMetaStrategy(nil) | ||
|
||
responsible := byMRMStrategy.IsResponsible(moduleInfo, moduleReleaseMeta) | ||
|
||
assert.False(t, responsible) | ||
} | ||
|
||
func Test_ByModuleReleaseMeta_Strategy_Lookup_ReturnsModuleTemplateInfo(t *testing.T) { | ||
moduleInfo := newModuleInfoBuilder().WithName("test-module").WithChannel("regular").Enabled().Build() | ||
kyma := builder.NewKymaBuilder().Build() | ||
moduleReleaseMeta := builder.NewModuleReleaseMetaBuilder(). | ||
WithModuleName("test-module"). | ||
WithName("test-module"). | ||
WithModuleChannelAndVersions([]v1beta2.ChannelVersionAssignment{ | ||
{ | ||
Channel: "regular", | ||
Version: "1.0.0", | ||
}, | ||
}). | ||
Build() | ||
moduleTemplate := builder.NewModuleTemplateBuilder(). | ||
WithName("test-module-1.0.0"). | ||
WithModuleName("test-module"). | ||
WithVersion("1.0.0"). | ||
WithChannel("none"). | ||
Build() | ||
byMRMStrategy := moduletemplateinfolookup.NewByModuleReleaseMetaStrategy(fakeClient( | ||
&v1beta2.ModuleTemplateList{ | ||
Items: []v1beta2.ModuleTemplate{ | ||
*moduleTemplate, | ||
}, | ||
}, | ||
)) | ||
|
||
moduleTemplateInfo := byMRMStrategy.Lookup(context.Background(), moduleInfo, kyma, moduleReleaseMeta) | ||
|
||
assert.NotNil(t, moduleTemplateInfo) | ||
assert.Equal(t, moduleTemplate.Name, moduleTemplateInfo.ModuleTemplate.Name) | ||
assert.Equal(t, moduleTemplate.Spec.ModuleName, moduleTemplateInfo.ModuleTemplate.Spec.ModuleName) | ||
assert.Equal(t, moduleTemplate.Spec.Version, moduleTemplateInfo.ModuleTemplate.Spec.Version) | ||
assert.Equal(t, moduleTemplate.Spec.Channel, moduleTemplateInfo.ModuleTemplate.Spec.Channel) | ||
} | ||
|
||
func fakeClient(mts *v1beta2.ModuleTemplateList) client.Client { | ||
scheme := machineryruntime.NewScheme() | ||
machineryutilruntime.Must(api.AddToScheme(scheme)) | ||
|
||
return fake.NewClientBuilder().WithScheme(scheme).WithLists(mts).Build() | ||
} |
Oops, something went wrong.