Skip to content

Instantly share code, notes, and snippets.

@serbrech
Last active April 10, 2019 07:32
Show Gist options
  • Save serbrech/2bf2fa7404069fce65d25cf26cb23cbe to your computer and use it in GitHub Desktop.
Save serbrech/2bf2fa7404069fce65d25cf26cb23cbe to your computer and use it in GitHub Desktop.
go plugins using a visitor pattern
package main
import (
"fmt"
)
// These interface define how core components can be visited by plugins
type AcceptNetworkPlugin interface {
Accept(plugin NetworkPlugin)
}
type AcceptKubeletPlugin interface {
Accept(plugin KubeletPlugin)
}
//-------------
//these interface determine the type of plugins that we allow for our system
type NetworkPlugin interface {
Apply(network *Network)
}
type KubeletPlugin interface {
Apply(kubelet *Kubelet)
}
//--------------
//Our dummy system implementation
type NetworkSpec struct {
CIDR string
}
type Network struct {
Spec NetworkSpec
}
func (n *Network) Accept(plugin NetworkPlugin) {
plugin.Apply(n)
}
type KubeletSpec struct {
AllowPrivileged bool
}
type Kubelet struct {
Spec KubeletSpec
}
func (n *Kubelet) Accept(plugin KubeletPlugin) {
plugin.Apply(n)
}
//Maybe some aggregate?
type Cluster struct {
Kubelet
Network
}
//------------
//The plugins, They implement the plugin interfaces provided by the system
type SpecialNetwork struct {
CIDROverride string
}
// Implements the Network plugin interface
func (n SpecialNetwork) Apply(network *Network) {
network.Spec.CIDR = n.CIDROverride
}
type PrivilegedKubelet struct {}
// Implements the Kubelet plugin interface
func (n PrivilegedKubelet) Apply(kubelet *Kubelet) {
kubelet.Spec.AllowPrivileged = true
}
//A program putting it all together, applying plugins
func main() {
//Setting up the default system
network := &Network{ Spec: NetworkSpec{ CIDR: "" }}
kubelet := &Kubelet{ Spec: KubeletSpec { }}
fmt.Println("Default system : ")
fmt.Println(network)
fmt.Println(kubelet)
//We provide the plugins to our system through some configuration
networkPlugin := SpecialNetwork{CIDROverride: "overriden!"}
kubeletPlugin := PrivilegedKubelet{}
//this is code that our pipeline calls while putting the configuration together.
// foreach componenet, find applicable plugins, apply accept them in order. Maybe interface should be Accept(Plugin p...)
//Note: a single plugin can implement both NetworkPlugin and KubeletPlugin and be applied to both.
//that allows to encapsulate the logic for a plugin that needs to change several area of our system
//without exposing that to our internal system.
network.Accept(networkPlugin)
kubelet.Accept(kubeletPlugin)
fmt.Println("\nAfter plugins")
fmt.Println(network)
fmt.Println(kubelet)
//Unfortunately, cluster.Accept(...) won't work if all components use the same Accept(...) method name to take plugins
//Although the method parameter are of different types (KubeletPlugin/NetworkPlugin)
//golang sees this the Accept call ambiguous, because it is the same method name on both embedded types
//may be solved with some clever code, some reflection maybe, hum...
//cluster := &Cluster{
// Kubelet: &Kubelet{ Spec: KubeletSpec{ }},
// Network: &Network{ Spec: NetworkSpec{ CIDR: "" }},
//}
//cluster.Accept(pk)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment