Question

General question

how can I design an interface that can support both

// v1beta1.Deployment
type Deployment struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Spec v1beta1.DeploymentSpec
    Status v1beta1.DeploymentStat
}
type DeploymentInterface interface {
    Create(*v1beta1.Deployment) (*v1beta1.Deployment, error)
    Update(*v1beta1.Deployment) (*v1beta1.Deployment, error)
    UpdateStatus(*v1beta1.Deployment) (*v1beta1.Deployment, error)
}
// v1.Deployment
type Deployment struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Spec v1.DeploymentSpec
    Status v1.DeploymentStat
}
type DeploymentInterface interface {
    Create(*v1.Deployment) (*v1.Deployment, error)
    Update(*v1.Deployment) (*v1.Deployment, error)
    UpdateStatus(*v1.Deployment) (*v1.Deployment, error)
}

which have different parameter and return types?


Details

The above two interfaces are from kubernetes go-client, they define different versions of the API. Since we have to support both of them for different versions of clusters we are running and I don't want to copy our application code for every version, I want to design an interface that can support different versions of Deployment.

The current code of our application has a lot of helper functions relying on the specific type, for an example:

func (s *KubeControllerService) deploymentCustomImage(deployment *v1beta1.Deployment, appGitBuildConfig *models.AppGitBuildConfig) *v1beta1.Deployment {
}

And we have hundreds of them. It would be very hard to support a new version by copying each function and impossible to maintain such code.

For what I know, since go's lack of generics, to support two different types the only viable way is to use interface. But i'm facing methods with different types of parameters and return values, I have no idea how to design for this scenario in go.

Was it helpful?

Solution

One way of doing can be like demonstrated as below. Here since I know my types I get the values out using the reflect package & calculate the area. You can run in on playground. Check out reflect package here


import (
    "fmt"
    "reflect"
)

type shape interface {
    area(interface{}, interface{}) interface{}
}

type rect struct{}
type tri struct{}

func (r rect)area(a,b interface{}) interface{} {
    fmt.Println(reflect.ValueOf(r).Type(), reflect.ValueOf(b).Kind())

    lenf := reflect.ValueOf(a).Float()
    widthf := reflect.ValueOf(b).Float()

    return lenf * widthf
}

func (t tri)area(a,b interface{}) interface{} {
    l := reflect.ValueOf(a).Int()
    h := reflect.ValueOf(b).Int()

    return h * l
}

func main() {
    r := rect{}
    t := tri{}
    fmt.Println("Area of rectangle:", r.area(4.5, 4.0))
    fmt.Println("Area of triangle:", t.area(4, 4))
}

So for your case the DeploymentInterface interface would look like

type DeploymentInterface interface {
    Create(interface{}) (interface{}, error)
    Update(interface{}) (interface{}, error)
    UpdateStatus(interface{}) (interface{}, error)
}
Licensed under: CC-BY-SA with attribution
scroll top