Skip to content

Instantly share code, notes, and snippets.

@isutton
Created June 15, 2020 15:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save isutton/fe3765d98e262b32f89a972bd88c436f to your computer and use it in GitHub Desktop.
Save isutton/fe3765d98e262b32f89a972bd88c436f to your computer and use it in GitHub Desktop.
New annotation handling
package annotations
import (
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
)
// bindingType encodes the medium the binding should deliver the configuration value.
type bindingType string
const (
// BindingTypeVolumeMount indicates the binding should happen through a volume mount.
BindingTypeVolumeMount bindingType = "volumemount"
// BindingTypeEnvVar indicates the binding should happen through environment variables.
BindingTypeEnvVar bindingType = "env"
)
// supportedBindingTypes contains all currently supported binding types.
var supportedBindingTypes = map[bindingType]bool{
BindingTypeVolumeMount: true,
BindingTypeEnvVar: true,
}
// dataPath is the path ConfigMap and Secret resources use to scope their data.
//
// note: it is currently used to provide a pointer to the "data" string, which is the location
// ConfigMap and Secret resources keep user data.
var dataPath = "data"
// result contains data that has been collected by an annotation handler.
type result struct {
// Data contains the annotation data collected by an annotation handler inside a deep structure
// with its root being the value specified in the Path field.
Data map[string]interface{}
// Type indicates where the Object field should be injected in the application; can be either
// "env" or "volumemount".
Type bindingType
// Path is the nested location the collected data can be found in the Data field.
Path string
// RawData is...
RawData map[string]interface{}
}
// handler should be implemented by types that want to offer a mechanism to provide binding data to
// the system.
type handler interface {
// Handle returns binding data.
Handle() (result, error)
}
type errHandlerNotFound string
func (e errHandlerNotFound) Error() string {
return fmt.Sprintf("could not find handler for annotation value %q", string(e))
}
func IsErrHandlerNotFound(err error) bool {
_, ok := err.(errHandlerNotFound)
return ok
}
// BuildHandler attempts to create an annotation handler for the given annotationKey and
// annotationValue. kubeClient is required by some annotation handlers, and an error is returned in
// the case it is required by an annotation handler but is not defined.
func BuildHandler(
kubeClient dynamic.Interface,
obj *unstructured.Unstructured,
annotationKey string,
annotationValue string,
restMapper meta.RESTMapper,
) (handler, error) {
bindingInfo, err := NewBindingInfo(annotationKey, annotationValue)
if err != nil {
return nil, err
}
val := bindingInfo.Value
switch {
case isAttribute(val):
return newAttributeHandler(bindingInfo, *obj), nil
case isSecret(val):
return newSecretHandler(kubeClient, bindingInfo, *obj, restMapper)
case isConfigMap(val):
return newConfigMapHandler(kubeClient, bindingInfo, *obj, restMapper)
case isNewAnnotationSyntax(val):
return newServiceBindingAnnotationHandler(val)
default:
return nil, errHandlerNotFound(val)
}
}
func isNewAnnotationSyntax(s string) bool {
_, err := buildPayload(s)
return err == nil
}
type serviceBindingAnnotationHandler struct {
payload payload
}
func (h serviceBindingAnnotationHandler) Handle() (result, error) {
panic("yolo")
}
func newServiceBindingAnnotationHandler(
client dynamic.Interface,
bi *bindingInfo,
resource unstructured.Unstructured,
relatedGroupVersionResource schema.GroupVersionResource,
inputPathPrefix *string,
restMapper meta.RESTMapper,
annotationValue string,
) (handler, error) {
// 1. decompose annotationValue into a type (Path, Type, BindAs, ...)
payload, err := buildPayload(annotationValue)
if err != nil {
return nil, err
}
dataPath := "data"
h, err := NewResourceHandler(
client,
bi,
resource,
payload.GetGroupVersionResource(),
&dataPath,
restMapper,
)
if err != nil {
return nil, err
}
h.stringValue = payload.stringValue
return h, nil
}
type payload struct {
bindAs string
typ string
stringValue func(interface{}) (string, error)
}
func (p *payload) GetGroupVersionResource() schema.GroupVersionResource {
if p.typ == "ConfigMap" {
return schema.GroupVersionResource{
Version: "v1",
Resource: "configmaps",
}
} else if p.typ == "Secret" {
return schema.GroupVersionResource{
Version: "v1",
Resource: "secrets",
}
}
return schema.GroupVersionResource{}
}
func buildPayload(s string) (*payload, error) {
panic("yolo")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment