Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for StatefulSets #1088

Merged
merged 9 commits into from
Sep 21, 2023
2 changes: 2 additions & 0 deletions common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ const (
ConfigStoragesKey = BaseKey + d + "storages"
//ConfigMinReplicasKey represents Ingress host Key
ConfigMinReplicasKey = BaseKey + d + "minreplicas"
//ConfigStatefulSetKey represents whether the IR should generate a StatefulSet
ConfigStatefulSetKey = "statefulset"
//ConfigPortsForServiceKeySegment represents the ports used for service
ConfigPortsForServiceKeySegment = "ports"
//ConfigPortForServiceKeySegment represents the port used for service
Expand Down
10 changes: 9 additions & 1 deletion transformer/compose/composegenerator.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ import (
composetypes "github.com/docker/cli/cli/compose/types"
"github.com/konveyor/move2kube/common"
"github.com/konveyor/move2kube/environment"
"github.com/konveyor/move2kube/transformer/kubernetes"
"github.com/konveyor/move2kube/transformer/kubernetes/irpreprocessor"
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
transformertypes "github.com/konveyor/move2kube/types/transformer"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -93,8 +95,14 @@ func (t *ComposeGenerator) Transform(newArtifacts []transformertypes.Artifact, a
logrus.Errorf("unable to load config for Transformer into %T : %s", ir, err)
continue
}

var clusterConfig collection.ClusterMetadata
if err := a.GetConfig(kubernetes.ClusterMetadata, &clusterConfig); err != nil {
logrus.Debugf("failed to load config for Transformer into %T . Error: %q", clusterConfig, err)
}

ir.Name = a.Name
preprocessedIR, err := irpreprocessor.Preprocess(ir)
preprocessedIR, err := irpreprocessor.Preprocess(ir, clusterConfig)
if err != nil {
logrus.Errorf("Unable to prepreocess IR : %s", err)
} else {
Expand Down
39 changes: 38 additions & 1 deletion transformer/kubernetes/apiresource/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ const (
replicationControllerKind string = "ReplicationController"
// daemonSetKind defines DaemonSet Kind
daemonSetKind string = "DaemonSet"
// statefulSetKind defines StatefulSet Kind
statefulSetKind string = "StatefulSet"
)

// Deployment handles all objects like a Deployment
Expand All @@ -54,7 +56,7 @@ type Deployment struct {

// getSupportedKinds returns kinds supported by the deployment
func (d *Deployment) getSupportedKinds() []string {
return []string{podKind, jobKind, common.DeploymentKind, deploymentConfigKind, replicationControllerKind, daemonSetKind}
return []string{podKind, jobKind, common.DeploymentKind, deploymentConfigKind, replicationControllerKind, daemonSetKind, statefulSetKind}
}

// createNewResources converts ir to runtime object
Expand All @@ -67,6 +69,11 @@ func (d *Deployment) createNewResources(ir irtypes.EnhancedIR, supportedKinds []
logrus.Errorf("Creating Daemonset even though not supported by target cluster.")
}
obj = d.createDaemonSet(service, targetCluster.Spec)
} else if service.StatefulSet {
if !common.IsPresent(supportedKinds, statefulSetKind) {
logrus.Errorf("Creating Statefulset even though not supported by target cluster.")
}
obj = d.createStatefulSet(service, targetCluster.Spec)
} else if service.RestartPolicy == core.RestartPolicyNever || service.RestartPolicy == core.RestartPolicyOnFailure {
if common.IsPresent(supportedKinds, jobKind) {
obj = d.createJob(service, targetCluster.Spec)
Expand Down Expand Up @@ -266,6 +273,36 @@ func (d *Deployment) createJob(service irtypes.Service, cluster collecttypes.Clu
return &pod
}

func (d *Deployment) createStatefulSet(service irtypes.Service, cluster collecttypes.ClusterMetadataSpec) *apps.StatefulSet {
podSpec := service.PodSpec
podSpec = irtypes.PodSpec(d.convertVolumesKindsByPolicy(core.PodSpec(podSpec), cluster))
podSpec.RestartPolicy = core.RestartPolicyAlways
meta := metav1.ObjectMeta{
Name: service.Name,
Labels: getPodLabels(service.Name, service.Networks),
Annotations: getAnnotations(service),
}
statefulset := apps.StatefulSet{
TypeMeta: metav1.TypeMeta{
Kind: statefulSetKind,
APIVersion: metav1.SchemeGroupVersion.String(),
},
ObjectMeta: meta,
Spec: apps.StatefulSetSpec{
Replicas: int32(service.Replicas),
Selector: &metav1.LabelSelector{
MatchLabels: getServiceLabels(meta.Name),
},
Template: core.PodTemplateSpec{
ObjectMeta: meta,
Spec: core.PodSpec(podSpec),
},
ServiceName: service.Name,
},
}
return &statefulset
}

// Conversions section

func (d *Deployment) toDeploymentConfig(meta metav1.ObjectMeta, podspec core.PodSpec, replicas int32, cluster collecttypes.ClusterMetadataSpec) *okdappsv1.DeploymentConfig {
Expand Down
2 changes: 1 addition & 1 deletion transformer/kubernetes/apiresource/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ func (d *Service) createService(service irtypes.Service) *core.Service {
Ports: ports,
},
}
if len(ports) == 0 {
if len(ports) == 0 || service.StatefulSet {
svc.Spec.ClusterIP = "None"
}
return svc
Expand Down
2 changes: 1 addition & 1 deletion transformer/kubernetes/argocdtransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func (t *ArgoCD) Transform(newArtifacts []transformertypes.Artifact, alreadySeen
continue
}
ir.Name = newArtifact.Name
preprocessedIR, err := irpreprocessor.Preprocess(ir)
preprocessedIR, err := irpreprocessor.Preprocess(ir, clusterConfig)
if err != nil {
logrus.Errorf("Unable to prepreocess IR : %s", err)
} else {
Expand Down
2 changes: 1 addition & 1 deletion transformer/kubernetes/buildconfigtransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func (t *BuildConfig) Transform(newArtifacts []transformertypes.Artifact, alread
continue
}
ir.Name = newArtifact.Name
preprocessedIR, err := irpreprocessor.Preprocess(ir)
preprocessedIR, err := irpreprocessor.Preprocess(ir, clusterConfig)
if err != nil {
logrus.Errorf("failed to prepreocess the IR. Error: %q", err)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package irpreprocessor

import (
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
core "k8s.io/kubernetes/pkg/apis/core"
)
Expand All @@ -25,7 +26,7 @@ import (
type imagePullPolicyPreprocessor struct {
}

func (ep imagePullPolicyPreprocessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
func (ep imagePullPolicyPreprocessor) preprocess(ir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error) {
for k, scObj := range ir.Services {
for i := range scObj.Containers {
scObj.Containers[i].ImagePullPolicy = core.PullAlways
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
"github.com/sirupsen/logrus"
core "k8s.io/kubernetes/pkg/apis/core"
)

func TestImagePullPolicyOptimizer(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel)
var clusterConfig collection.ClusterMetadata


t.Run("IR with no services", func(t *testing.T) {
// Setup
Expand All @@ -35,7 +38,7 @@ func TestImagePullPolicyOptimizer(t *testing.T) {
want := getIRWithoutServices()

// Test
actual, err := imagePullPolicyPreprocessor.preprocess(ir)
actual, err := imagePullPolicyPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand All @@ -51,7 +54,7 @@ func TestImagePullPolicyOptimizer(t *testing.T) {
want := getIRWithServicesAndWithoutContainers()

// Test
actual, err := imagePullPolicyPreprocessor.preprocess(ir)
actual, err := imagePullPolicyPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand Down Expand Up @@ -83,7 +86,7 @@ func TestImagePullPolicyOptimizer(t *testing.T) {
want := getIRWithImagePullPolicySetAsAlways()

// Test
actual, err := imagePullPolicyPreprocessor.preprocess(ir)
actual, err := imagePullPolicyPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand All @@ -99,7 +102,7 @@ func TestImagePullPolicyOptimizer(t *testing.T) {
want := getIRWithImagePullPolicySetAsAlways()

// Test
actual, err := imagePullPolicyPreprocessor.preprocess(ir)
actual, err := imagePullPolicyPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand Down
9 changes: 8 additions & 1 deletion transformer/kubernetes/irpreprocessor/ingresspreprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/konveyor/move2kube/common"
"github.com/konveyor/move2kube/qaengine"
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
"github.com/spf13/cast"
core "k8s.io/kubernetes/pkg/apis/core"
Expand All @@ -31,7 +32,7 @@ import (
type ingressPreprocessor struct {
}

func (opt *ingressPreprocessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
func (opt *ingressPreprocessor) preprocess(ir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error) {
for serviceName, service := range ir.Services {
tempService := ir.Services[serviceName]
for portForwardingIdx, portForwarding := range service.ServiceToPodPortForwardings {
Expand All @@ -41,6 +42,12 @@ func (opt *ingressPreprocessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
if portForwarding.ServiceRelPath == "" {
portForwarding.ServiceRelPath = "/" + serviceName
}
// Create Headless Services for services that are to be converted into StatefulSets
if service.StatefulSet {
portForwarding.ServiceType = core.ServiceTypeClusterIP
tempService.ServiceToPodPortForwardings[portForwardingIdx] = portForwarding
continue
}
noneServiceType := "Don't create service"
portKeyPart := common.JoinQASubKeys(common.ConfigServicesKey, `"`+serviceName+`"`, `"`+cast.ToString(portForwarding.ServicePort.Number)+`"`)
options := []string{common.IngressKind, string(core.ServiceTypeLoadBalancer), string(core.ServiceTypeNodePort), string(core.ServiceTypeClusterIP), noneServiceType}
Expand Down
10 changes: 6 additions & 4 deletions transformer/kubernetes/irpreprocessor/irpreprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,31 @@
package irpreprocessor

import (
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
"github.com/sirupsen/logrus"
)

// irpreprocessor optimizes the configuration
type irpreprocessor interface {
preprocess(sourceir irtypes.IR) (irtypes.IR, error)
preprocess(sourceir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error)
}

// getIRPreprocessors returns optimizers
func getIRPreprocessors() []irpreprocessor {
var l = []irpreprocessor{new(mergePreprocessor), new(normalizeCharacterPreprocessor), new(ingressPreprocessor), new(replicaPreprocessor), new(imagePullPolicyPreprocessor), new(registryPreProcessor)}
var l = []irpreprocessor{new(mergePreprocessor), new(normalizeCharacterPreprocessor), new(statefulsetPreprocessor), new(ingressPreprocessor), new(replicaPreprocessor),
new(imagePullPolicyPreprocessor), new(registryPreProcessor)}
return l
}

// Preprocess preprocesses IR before application artifacts are generated
func Preprocess(ir irtypes.IR) (irtypes.IR, error) {
func Preprocess(ir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error) {
optimizers := getIRPreprocessors()
logrus.Debug("Begin Optimization")
for _, o := range optimizers {
logrus.Debugf("[%T] Begin Optimization", o)
var err error
ir, err = o.preprocess(ir)
ir, err = o.preprocess(ir, targetCluster)
if err != nil {
logrus.Warnf("[%T] Failed : %s", o, err.Error())
} else {
Expand Down
3 changes: 2 additions & 1 deletion transformer/kubernetes/irpreprocessor/mergepreprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package irpreprocessor

import (
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
core "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/networking"
Expand All @@ -27,7 +28,7 @@ type mergePreprocessor struct {
}

// Preprocesses the port forwardings
func (opt *mergePreprocessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
func (opt *mergePreprocessor) preprocess(ir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error) {
for serviceName, service := range ir.Services {
service.Containers = opt.mergeContainers(service.Containers)
pfs := service.ServiceToPodPortForwardings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"regexp"
"strings"

"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
core "k8s.io/kubernetes/pkg/apis/core"
)
Expand All @@ -28,7 +29,7 @@ import (
type normalizeCharacterPreprocessor struct {
}

func (po normalizeCharacterPreprocessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
func (po normalizeCharacterPreprocessor) preprocess(ir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error) {
//TODO: Make this generic to ensure all fields have valid names
for i := range ir.Services {
for j := range ir.Services[i].Containers {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
"github.com/sirupsen/logrus"
core "k8s.io/kubernetes/pkg/apis/core"
Expand Down Expand Up @@ -67,6 +68,7 @@ func TestStripQuotation(t *testing.T) {

func TestOptimize(t *testing.T) {
logrus.SetLevel(logrus.DebugLevel)
var clusterConfig collection.ClusterMetadata

t.Run("IR with no services", func(t *testing.T) {
// Setup
Expand All @@ -75,7 +77,7 @@ func TestOptimize(t *testing.T) {
want := getIRWithoutServices()

// Test
actual, err := normalizeCharacterPreprocessor.preprocess(ir)
actual, err := normalizeCharacterPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand All @@ -91,7 +93,7 @@ func TestOptimize(t *testing.T) {
want := getIRWithServicesAndWithoutContainers()

// Test
actual, err := normalizeCharacterPreprocessor.preprocess(ir)
actual, err := normalizeCharacterPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand All @@ -107,7 +109,7 @@ func TestOptimize(t *testing.T) {
want := getIRWithServicesAndContainersWithoutEnv()

// Test
actual, err := normalizeCharacterPreprocessor.preprocess(ir)
actual, err := normalizeCharacterPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand All @@ -123,7 +125,7 @@ func TestOptimize(t *testing.T) {
want := getIRWithServicesAndContainersWithValidEnv()

// Test
actual, err := normalizeCharacterPreprocessor.preprocess(ir)
actual, err := normalizeCharacterPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand Down Expand Up @@ -163,7 +165,7 @@ func TestOptimize(t *testing.T) {
want := getExpectedIR()

// Test
actual, err := normalizeCharacterPreprocessor.preprocess(ir)
actual, err := normalizeCharacterPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand Down Expand Up @@ -202,7 +204,7 @@ func TestOptimize(t *testing.T) {
want := getExpectedIRWithAffinityInContainer()

// Test
actual, err := normalizeCharacterPreprocessor.preprocess(ir)
actual, err := normalizeCharacterPreprocessor.preprocess(ir, clusterConfig)
if err != nil {
t.Fatal("Failed to get the expected. Error:", err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
dockerclitypes "github.com/docker/cli/cli/config/types"
"github.com/konveyor/move2kube/common"
"github.com/konveyor/move2kube/qaengine"
"github.com/konveyor/move2kube/types/collection"
irtypes "github.com/konveyor/move2kube/types/ir"
"github.com/konveyor/move2kube/types/qaengine/commonqa"
"github.com/sirupsen/logrus"
Expand All @@ -51,7 +52,7 @@ const (
imagePullSecretSuffix = "-imagepullsecret"
)

func (p registryPreProcessor) preprocess(ir irtypes.IR) (irtypes.IR, error) {
func (p registryPreProcessor) preprocess(ir irtypes.IR, targetCluster collection.ClusterMetadata) (irtypes.IR, error) {
// find all the new images that we are going to create

newImageNames := []string{}
Expand Down
Loading