This repository is an example of custom Kloud provider plugin.
Kloud is a backend behind Koding that is responsible for bootstrapping, building and destroying user stacks and managing access to each machine instance within those stacks.
A new Kloud provider can be added by simply clonning your provider plugin into kloud/provider directory and rebuilding Kloud.
koding $ git clone git@github.com:kloud/kloud-provider-example go/src/koding/kites/kloud/provider/example
koding $ ./go/build.shA Kloud provider is responsible for composing a single Terraform template from multiple data sources:
- stack template
- credentials
- bootstrapped resources
A single Kloud provider validates credentials provided by a user, bootstraps a stack by creating provider-specific, persistant resources and provisions a Klient service for each instance built within a stack. The Klient service is used to connect remote machine to Koding allowing for webterm sessions in the browser, machine-sharing with other Koding users and starting / stopping the machine itself.
This repository contains a documented example of a Kloud provider plugin - example.go.
The Koding repository contains more examples of Kloud providers:
Go services in Koding repository use currently project-based GOPATH, that's why ensure you point your GOPATH to the go directory inside Koding repository:
~ $ export GOPATH=~/github.com/koding/koding/goA project structure of your Kloud provider may look like the following:
your/
├── your.go <-- registers *provider.Provider
├── machine.go <-- defines *Machine struct
├── schema.go <-- defines *Credential, *Bootstrap and *Metadata structs
└── stack.go <-- defines *Stack struct
example.goregisters provider definition (like this one)
The content of this file is:
package your
import (
"koding/kites/kloud/stack"
"koding/kites/kloud/stack/provider"
)
var p = &provider.Provider{
Name: "your",
ResourceName: "instance",
// Machine type is defined in machine.go
Machine: func(bm *provider.BaseMachine) (provider.Machine, error) {
return &Machine{BaseMachine: bm}, nil
},
// Stack type is defined in stack.go
Stack: func(bs *provider.BaseStack) (provider.Stack, error) {
return &Stack{BaseStack: bs}, nil
},
// Schema value is defined in schema.go
Schema: Schema,
}
func init() {
provider.Register(p)
}schema.godefines models which are used to persist provider data (like this one)
And its contents:
package your
import "koding/kites/kloud/stack/provider"
var Schema = &provider.Schema{
NewCredential: func() interface{} { return &Credential{} },
NewBootstrap: func() interface{} { return &Bootstrap{} },
NewMetadata: func(*stack.Machine) interface{} { &Metadata{} },
}
type Credential struct {
User string
Pass string
}
type Bootstrap struct {
PersistentResourceName string
}
type Metadata struct {
MachineRegion string
MachineCNAME string
}If any of the schema types implement the following interface:
type Validator interface {
Valid() error
}the Valid() method is going to be called after reading the value from Koding database / safe store in order to validate it.
More details on schema can be found here, here and here.
stack.godefines a Stack struct which implements theprovider.Stackinterface
The stub definition looks like:
package your
import (
"errors"
"koding/kites/kloud/stack"
"koding/kites/kloud/stack/provider"
)
var errNotImplemented = errors.New("not implemented")
type Stack struct {
*provider.BaseStack
}
func (*Stack) VerifyCredential(*stack.Credential) error {
return errNotImplemented
}
func (*Stack) BootstrapTemplates(*stack.Credential) ([]*stack.Template, error) {
return nil, errNotImplemented
}
func (*Stack) ApplyTemplate(*stack.Credential) (*stack.Template, error) {
return nil, errNotImplemented
}More details on expected behavior of each method can be found here, here and here.
- and
machine.go, which defines *Machine for controlling single remote machine
The stub:
package your
import (
"errors"
"koding/kites/kloud/machinestate"
"koding/kites/kloud/stack/provider"
"golang.org/x/net/context"
)
var errNotImplemented = errors.New("not implemented")
type Machine struct {
*provider.BaseMachine
}
func (*Machine) Start(context.Context) (metadata interface{}, err error) {
return nil, errNotImplemented
}
func (*Machine) Stop(context.Context) (metadata interface{}, err error) {
return nil, errNotImplemented
}
func (*Machine) Info(context.Context) (state machinestate.State, metadata interface{}, err error) {
return 0, nil, errNotImplemented
}More details here and an example of AWS implementation.
The *provider.BaseMachine and *provider.BaseStack API should be considered not stable, thus a subject to change. Usually it means a rename here and there or new fields that remove the boilerplate even further.
Relavant issues: