Lukas Knuth
Lukas Knuth

Reputation: 25755

Polymorphism with struct from outside my package

I'm trying to build a method that, using the Kubernetes client-go library, fetches and returns the actual Resources for a given *metav1.OwnerReference. I have this:

func fetchResource(ref *metav1.OwnerReference, options *RequestOptions) (*metav1.ObjectMeta, error) {
    switch ref.Kind {
    case "ReplicaSet":
        return options.Clientset.AppsV1().ReplicaSets(options.Namespace).Get(options.Context, ref.Name, metav1.GetOptions{})
    case "Deployment":
        return options.Clientset.AppsV1().Deployments(options.Namespace).Get(options.Context, ref.Name, metav1.GetOptions{})
    case "Job":
        fallthrough
    // more stuff...
    default:
        return nil, nil
    }
}

This code does not compile because:

cannot use options.Clientset.AppsV1().ReplicaSets(options.Namespace).Get(options.Context, ref.Name, (metav1.GetOptions literal)) (value of type *"k8s.io/api/apps/v1".ReplicaSet) as *"k8s.io/apimachinery/pkg/apis/meta/v1".ObjectMeta value in return statement

My guess was that since the documentation says that basically all resources embedd the metav1.ObjectMeta, I could use it as a return type.

I tried creating and returning an interface instead, but realized I can't implement it for types outside my package:

type K8sResource interface {
    Name() string
    Kind() string
    OwnerReferences() []metav1.OwnerReference
}

func (pod *corev1.Pod) Name() string {
    return pod.Name
}
func (pod *corev1.Pod) Kind() string {
    return pod.Kind
}
func (pod *corev1.Pod) OwnerReferences() []metav1.OwnerReference {
    return pod.OwnerReferences
}

This code does not compile because:

invalid receiver *"k8s.io/api/core/v1".Pod (type not defined in this package)

What would be the idiomatic and correct solution here?

Upvotes: 0

Views: 383

Answers (1)

mkopriva
mkopriva

Reputation: 38233

If you want to return the imported types as an interface that they don't already implement, you can wrap them in types that do implement it.

For example:

type K8sResource interface {
    Name() string
    Kind() string
    OwnerReferences() []metav1.OwnerReference
}
type replicaSet struct{ *v1.ReplicaSet }

func (s replicaSet) Name() string {
    return s.ReplicaSet.Name
}
func (s replicaSet) Kind() string {
    return s.ReplicaSet.Kind
}
func (s replicaSet) OwnerReferences() []metav1.OwnerReference {
    return s.ReplicaSet.OwnerReferences
}
func fetchResource(ref *metav1.OwnerReference, options *RequestOptions) (K8sResource, error) {
    switch ref.Kind {
    case "ReplicaSet":
        res, err := options.Clientset.AppsV1().ReplicaSets(options.Namespace).Get(options.Context, ref.Name, metav1.GetOptions{})
        if err != nil {
            return nil, err
        }
        return replicaSet{res}, nil // wrap it up
    case "Pod":
        res, err := options.Clientset.AppsV1().Pods(options.Namespace).Get(options.Context, ref.Name, metav1.GetOptions{})
        if err != nil {
            return nil, err
        }
        return pod{res}, nil // wrap it up
    case "Job":
        fallthrough
    // more stuff...
    default:
        return nil, nil
    }
}

Upvotes: 2

Related Questions