Implement the Fission provider scaffolding; and Environments
This commit is contained in:
parent
936d3f45e6
commit
366e236853
4 changed files with 291 additions and 0 deletions
21
lib/kube-fission/provider/context.go
Normal file
21
lib/kube-fission/provider/context.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Copyright 2017 Pulumi, Inc. All rights reserved.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/fission/fission/controller/client"
|
||||
"github.com/pulumi/coconut/pkg/util/contract"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
Fission *client.Client // the Fission controller client.
|
||||
}
|
||||
|
||||
func NewContext() *Context {
|
||||
// TODO[pulumi/coconut#117]: fetch the client from config rather than environment variables.
|
||||
url := os.Getenv("FISSION_URL")
|
||||
contract.Assertf(url != "", "Missing FISSION_URL environment variable")
|
||||
return &Context{Fission: client.MakeClient(url)}
|
||||
}
|
109
lib/kube-fission/provider/environment.go
Normal file
109
lib/kube-fission/provider/environment.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2017 Pulumi, Inc. All rights reserved.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/fission/fission"
|
||||
pbempty "github.com/golang/protobuf/ptypes/empty"
|
||||
pbstruct "github.com/golang/protobuf/ptypes/struct"
|
||||
"github.com/pulumi/coconut/pkg/resource"
|
||||
"github.com/pulumi/coconut/pkg/tokens"
|
||||
"github.com/pulumi/coconut/pkg/util/contract"
|
||||
"github.com/pulumi/coconut/pkg/util/mapper"
|
||||
"github.com/pulumi/coconut/sdk/go/pkg/cocorpc"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
const Environment = tokens.Type("kube-fission:environment:Environment")
|
||||
|
||||
// NewEnvironmentProvider creates a provider that handles S3 environment operations.
|
||||
func NewEnvironmentProvider(ctx *Context) cocorpc.ResourceProviderServer {
|
||||
return &envProvider{ctx}
|
||||
}
|
||||
|
||||
type envProvider struct {
|
||||
ctx *Context
|
||||
}
|
||||
|
||||
// Check validates that the given property bag is valid for a resource of the given type.
|
||||
func (p *envProvider) Check(ctx context.Context, req *cocorpc.CheckRequest) (*cocorpc.CheckResponse, error) {
|
||||
// Read in the properties, create and validate a new group, and return the failures (if any).
|
||||
contract.Assert(req.GetType() == string(Environment))
|
||||
_, _, result := unmarshalEnvironment(req.GetProperties())
|
||||
return resource.NewCheckResponse(result), nil
|
||||
}
|
||||
|
||||
// Name names a given resource. Sometimes this will be assigned by a developer, and so the provider
|
||||
// simply fetches it from the property bag; other times, the provider will assign this based on its own algorithm.
|
||||
// In any case, resources with the same name must be safe to use interchangeably with one another.
|
||||
func (p *envProvider) Name(ctx context.Context, req *cocorpc.NameRequest) (*cocorpc.NameResponse, error) {
|
||||
contract.Failf("Kube-Fission top-level dispatcher should have handled this RPC")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *envProvider) Create(ctx context.Context, req *cocorpc.CreateRequest) (*cocorpc.CreateResponse, error) {
|
||||
contract.Assert(req.GetType() == string(Environment))
|
||||
|
||||
// Read in the properties given by the request, validating as we go; if any fail, reject the request.
|
||||
env, _, err := unmarshalEnvironment(req.GetProperties())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Perform the operation by contacting the controller.
|
||||
fmt.Printf("Creating Fission environment '%v'\n", env.Metadata.Name)
|
||||
meta, decerr := p.ctx.Fission.EnvironmentCreate(&env)
|
||||
if decerr != nil {
|
||||
return nil, decerr
|
||||
}
|
||||
fmt.Printf("Fission Environment '%v' created: version=%v\n", meta.Name, meta.Uid)
|
||||
return &cocorpc.CreateResponse{Id: meta.Name}, nil
|
||||
}
|
||||
|
||||
// Read reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *envProvider) Read(ctx context.Context, req *cocorpc.ReadRequest) (*cocorpc.ReadResponse, error) {
|
||||
contract.Assert(req.GetType() == string(Environment))
|
||||
return nil, errors.New("Not yet implemented")
|
||||
}
|
||||
|
||||
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
|
||||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *envProvider) Update(ctx context.Context, req *cocorpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(Environment))
|
||||
return nil, errors.New("Not yet implemented")
|
||||
}
|
||||
|
||||
// UpdateImpact checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *envProvider) UpdateImpact(
|
||||
ctx context.Context, req *cocorpc.UpdateRequest) (*cocorpc.UpdateImpactResponse, error) {
|
||||
contract.Assert(req.GetType() == string(Environment))
|
||||
return nil, errors.New("Not yet implemented")
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *envProvider) Delete(ctx context.Context, req *cocorpc.DeleteRequest) (*pbempty.Empty, error) {
|
||||
contract.Assert(req.GetType() == string(Environment))
|
||||
|
||||
id := req.GetId()
|
||||
fmt.Printf("Deleting Fission Environment '%v'\n", id)
|
||||
meta := &fission.Metadata{Name: id}
|
||||
if err := p.ctx.Fission.EnvironmentDelete(meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Fission Environment '%v' deleted\n", id)
|
||||
|
||||
return &pbempty.Empty{}, nil
|
||||
}
|
||||
|
||||
// unmarshalEnvironment decodes and validates an environment property bag.
|
||||
func unmarshalEnvironment(v *pbstruct.Struct) (fission.Environment, resource.PropertyMap, mapper.DecodeError) {
|
||||
var env fission.Environment
|
||||
props := resource.UnmarshalProperties(v)
|
||||
err := mapper.MapIU(props.Mappable(), &env)
|
||||
return env, props, err
|
||||
}
|
38
lib/kube-fission/provider/main.go
Normal file
38
lib/kube-fission/provider/main.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2017 Pulumi, Inc. All rights reserved.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/pulumi/coconut/pkg/util/rpcutil"
|
||||
"github.com/pulumi/coconut/sdk/go/pkg/cocorpc"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Fire up a gRPC server, letting the kernel choose a free port for us.
|
||||
port, done, err := rpcutil.Serve(0, []func(*grpc.Server) error{
|
||||
func(srv *grpc.Server) error {
|
||||
prov, err := NewProvider()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Kube-Fission resource provider: %v", err)
|
||||
}
|
||||
cocorpc.RegisterResourceProviderServer(srv, prov)
|
||||
return nil
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fatal: %v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
|
||||
// The resource provider protocol requires that we now write out the port we have chosen to listen on.
|
||||
fmt.Printf("%d\n", port)
|
||||
|
||||
// Finally, wait for the server to stop serving.
|
||||
if err := <-done; err != nil {
|
||||
fmt.Fprintf(os.Stderr, "fatal: %v\n", err)
|
||||
}
|
||||
}
|
123
lib/kube-fission/provider/provider.go
Normal file
123
lib/kube-fission/provider/provider.go
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2017 Pulumi, Inc. All rights reserved.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fission/fission"
|
||||
pbempty "github.com/golang/protobuf/ptypes/empty"
|
||||
"github.com/pulumi/coconut/pkg/resource"
|
||||
"github.com/pulumi/coconut/pkg/tokens"
|
||||
"github.com/pulumi/coconut/pkg/util/contract"
|
||||
"github.com/pulumi/coconut/pkg/util/mapper"
|
||||
"github.com/pulumi/coconut/sdk/go/pkg/cocorpc"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// provider implements the Fission resource package functionality behind a gRPC interface.
|
||||
type Provider struct {
|
||||
impls map[tokens.Type]cocorpc.ResourceProviderServer
|
||||
}
|
||||
|
||||
// NewProvider creates a new provider instance with server objects registered for every resource type.
|
||||
func NewProvider() (*Provider, error) {
|
||||
ctx := NewContext()
|
||||
return &Provider{
|
||||
impls: map[tokens.Type]cocorpc.ResourceProviderServer{
|
||||
Environment: NewEnvironmentProvider(ctx),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ cocorpc.ResourceProviderServer = (*Provider)(nil)
|
||||
|
||||
// Check validates that the given property bag is valid for a resource of the given type.
|
||||
func (p *Provider) Check(ctx context.Context, req *cocorpc.CheckRequest) (*cocorpc.CheckResponse, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.Check(ctx, req)
|
||||
}
|
||||
return nil, fmt.Errorf("Unrecognized resource type (Check): %v", t)
|
||||
}
|
||||
|
||||
// Name names a given resource. Sometimes this will be assigned by a developer, and so the provider
|
||||
// simply fetches it from the property bag; other times, the provider will assign this based on its own algorithm.
|
||||
// In any case, resources with the same name must be safe to use interchangeably with one another.
|
||||
func (p *Provider) Name(ctx context.Context, req *cocorpc.NameRequest) (*cocorpc.NameResponse, error) {
|
||||
// First, validate that this is a type we understand.
|
||||
t := tokens.Type(req.GetType())
|
||||
if _, has := p.impls[t]; !has {
|
||||
return nil, fmt.Errorf("Unrecognized resource type (Name): %v", t)
|
||||
}
|
||||
|
||||
// All Fission names are taken from the Metadata header:
|
||||
//
|
||||
// {
|
||||
// "metadata": {
|
||||
// "name": "<NAME GOES HERE>"
|
||||
//
|
||||
// So, no need to ask the individual resource providers to do this; we can do it once and for all here.
|
||||
var record struct {
|
||||
Metadata fission.Metadata `json:"metadata"`
|
||||
}
|
||||
|
||||
// Unmarshal the property bag into our struct.
|
||||
props := req.GetProperties()
|
||||
uprops := resource.UnmarshalProperties(props)
|
||||
if err := mapper.MapIU(uprops.Mappable(), &record); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we got here, the name is valid; return it.
|
||||
contract.Assert(record.Metadata.Name != "")
|
||||
return &cocorpc.NameResponse{Name: record.Metadata.Name}, nil
|
||||
}
|
||||
|
||||
// Create allocates a new instance of the provided resource and returns its unique ID afterwards. (The input ID
|
||||
// must be blank.) If this call fails, the resource must not have been created (i.e., it is "transacational").
|
||||
func (p *Provider) Create(ctx context.Context, req *cocorpc.CreateRequest) (*cocorpc.CreateResponse, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.Create(ctx, req)
|
||||
}
|
||||
return nil, fmt.Errorf("Unrecognized resource type (Create): %v", t)
|
||||
}
|
||||
|
||||
// Read reads the instance state identified by ID, returning a populated resource object, or an error if not found.
|
||||
func (p *Provider) Read(ctx context.Context, req *cocorpc.ReadRequest) (*cocorpc.ReadResponse, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.Read(ctx, req)
|
||||
}
|
||||
return nil, fmt.Errorf("Unrecognized resource type (Read): %v", t)
|
||||
}
|
||||
|
||||
// Update updates an existing resource with new values. Only those values in the provided property bag are updated
|
||||
// to new values. The resource ID is returned and may be different if the resource had to be recreated.
|
||||
func (p *Provider) Update(ctx context.Context, req *cocorpc.UpdateRequest) (*pbempty.Empty, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.Update(ctx, req)
|
||||
}
|
||||
return nil, fmt.Errorf("Unrecognized resource type (Update): %v", t)
|
||||
}
|
||||
|
||||
// UpdateImpact checks what impacts a hypothetical update will have on the resource's properties.
|
||||
func (p *Provider) UpdateImpact(
|
||||
ctx context.Context, req *cocorpc.UpdateRequest) (*cocorpc.UpdateImpactResponse, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.UpdateImpact(ctx, req)
|
||||
}
|
||||
return nil, fmt.Errorf("Unrecognized resource type (UpdateImpact): %v", t)
|
||||
}
|
||||
|
||||
// Delete tears down an existing resource with the given ID. If it fails, the resource is assumed to still exist.
|
||||
func (p *Provider) Delete(ctx context.Context, req *cocorpc.DeleteRequest) (*pbempty.Empty, error) {
|
||||
t := tokens.Type(req.GetType())
|
||||
if prov, has := p.impls[t]; has {
|
||||
return prov.Delete(ctx, req)
|
||||
}
|
||||
return nil, fmt.Errorf("Unrecognized resource type (Delete): %v", t)
|
||||
}
|
Loading…
Reference in a new issue