Skip to content
This repository was archived by the owner on Aug 15, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 46 additions & 7 deletions go/src/koding/kites/kloud/provider/azure/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func newMetadata(m *stack.Machine) interface{} {
}

if cred, ok := m.Credential.Credential.(*Cred); ok {
meta.Location = cred.Location
meta.Location = string(cred.Location)
}

return meta
Expand All @@ -81,14 +81,53 @@ type Subscription struct {
ID string `xml:"Id,attr"`
}

type (
StorageType string
LocationType string
)

var (
_ stack.Enumer = StorageType("")
_ stack.Enumer = LocationType("")
)

var (
Storages = []stack.Enum{
{Title: "Locally redundant storage (LRS)", Value: "Standard_LRS"},
{Title: "Zone-redundant storage (ZRS)", Value: "Standard_ZRS"},
{Title: "Geo-redundant storage (GRS)", Value: "Standard_GRS"},
{Title: "Read-access geo-redundant storage (RA-GRS)", Value: "Standard_RAGRS"},
{Title: "Premium Locally redundant storage (P_LRS)", Value: "Premium_LRS"},
}

Locations = []stack.Enum{
{Title: "East US", Value: "East US"},
{Title: "East US 2", Value: "East US 2"},
{Title: "West US", Value: "West US"},
{Title: "Central US", Value: "Central US"},
{Title: "South Central US", Value: "South Central US"},
{Title: "North Europe", Value: "North Europe"},
{Title: "West Europe", Value: "West Europe"},
{Title: "East Asia", Value: "East Asia"},
{Title: "Southeast Asia", Value: "Southeast Asia"},
{Title: "Japan East", Value: "Japan East"},
{Title: "Japan West", Value: "Japan West"},
{Title: "North Central US", Value: "North Central US"},
{Title: "Brazil South", Value: "Brazil South"},
}
)

func (StorageType) Enums() []stack.Enum { return Storages }
func (LocationType) Enums() []stack.Enum { return Locations }

// Cred represents jCredentialDatas.meta for "azure" provider.
type Cred struct {
PublishSettings string `json:"publish_settings" bson:"publish_settings" hcl:"publish_settings"` // required
SubscriptionID string `json:"subscription_id,omitempty" bson:"subscription_id,omitempty" hcl:"subscription_id"` // required if PublishSettings contains multiple subscriptions
Location string `json:"location,omitempty" bson:"location,omitempty" hcl:"location"` // by default "East US 2"
Storage string `json:"storage,omitempty" bson:"storage,omitempty" hcl:"storage"` // by default "Standard_LRS"
SSHKeyThumbprint string `json:"ssh_key_thumbprint,omitempty" bson:"ssh_key_thumbprint" hcl:"ssh_key_thumbprint"`
Password string `json:"password" bson:"password" hcl:"password"`
PublishSettings string `json:"publish_settings" bson:"publish_settings" hcl:"publish_settings"` // required
SubscriptionID string `json:"subscription_id,omitempty" bson:"subscription_id,omitempty" hcl:"subscription_id"` // required if PublishSettings contains multiple subscriptions
Location LocationType `json:"location,omitempty" bson:"location,omitempty" hcl:"location"` // by default "East US 2"
Storage StorageType `json:"storage,omitempty" bson:"storage,omitempty" hcl:"storage"` // by default "Standard_LRS"
SSHKeyThumbprint string `json:"ssh_key_thumbprint,omitempty" bson:"ssh_key_thumbprint" hcl:"ssh_key_thumbprint"`
Password string `json:"password" bson:"password" hcl:"password"`
}

var _ stack.Validator = (*Cred)(nil)
Expand Down
2 changes: 1 addition & 1 deletion go/src/koding/kites/kloud/provider/azure/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ func (s *Stack) BootstrapTemplates(c *stack.Credential) ([]*stack.Template, erro
cfg := &BootstrapConfig{
TeamSlug: s.BootstrapArg().GroupName,
HostedServiceName: "koding-hs-" + c.Identifier,
StorageType: cred.Storage,
StorageType: string(cred.Storage),
AddressSpace: boot.addressSpace(),
StorageServiceName: substringN(strings.ToLower("kodings"+c.Identifier), 24),
SecurityGroupName: "koding-sg-" + c.Identifier,
Expand Down
65 changes: 35 additions & 30 deletions go/src/koding/kites/kloud/provider/do/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,35 @@ type Bootstrap struct {
KeyFingerprint string `json:"key_fingerprint" bson:"key_fingerprint" hcl:"key_fingerprint"`
}

// Region represents a DigitalOcean region
type Region string
// RegionType represents a DigitalOcean region
type RegionType string

var _ stack.Enumer = RegionType("")

var Regions = []stack.Enum{
{Title: "Amsterdam 1", Value: "ams1"},
{Title: "Amsterdam 2", Value: "ams2"},
{Title: "Amsterdam 3", Value: "ams3"},
{Title: "Bangalore 1", Value: "blr1"},
{Title: "France 1", Value: "fra1"},
{Title: "London 1", Value: "lon1"},
{Title: "New York 1", Value: "nyc1"},
{Title: "New York 2", Value: "nyc2"},
{Title: "New York 3", Value: "nyc3"},
{Title: "San Francisco 1", Value: "sfo1"},
{Title: "San Francisco 2", Value: "sfo2"},
{Title: "Singapore 1", Value: "sgp1"},
{Title: "Toronto 1", Value: "tor1"},
}

func (RegionType) Enums() []stack.Enum { return Regions }

// Metadata represent the data that is stored on Koding's DB storage
type Metadata struct {
DropletID int `json:"droplet_id" bson:"droplet_id" hcl:"droplet_id"`
Region Region `json:"region" bson:"region" hcl:"region"`
Size string `json:"size" bson:"size" hcl:"size"`
Image string `json:"image" bson:"image" hcl:"image"`
DropletID int `json:"droplet_id" bson:"droplet_id" hcl:"droplet_id"`
Region RegionType `json:"region" bson:"region" hcl:"region"`
Size string `json:"size" bson:"size" hcl:"size"`
Image string `json:"image" bson:"image" hcl:"image"`
}

func newSchema() *provider.Schema {
Expand All @@ -83,7 +103,7 @@ func newMetadata(m *stack.Machine) interface{} {
meta := &Metadata{
Size: m.Attributes["size"],
Image: m.Attributes["image"],
Region: Region(m.Attributes["region"]),
Region: RegionType(m.Attributes["region"]),
}

if id, err := strconv.Atoi(m.Attributes["id"]); err == nil {
Expand Down Expand Up @@ -115,9 +135,8 @@ func (m *Metadata) Valid() error {
return errors.New("region cannot be empty")
}

if !m.Region.isValid() {
return fmt.Errorf("region %q is not valid. Valid regions can be one of the following: %v",
m.Region, validRegions)
if err := m.Region.Valid(); err != nil {
return err
}

if m.Image == "" {
Expand Down Expand Up @@ -148,26 +167,12 @@ func (b *Bootstrap) Valid() error {
return nil
}

// isValid checks whether the given region is valid or not
func (r Region) isValid() bool {
for _, validRegion := range validRegions {
if validRegion == string(r) {
return true
// Valid implements the stack.Validator interface.
func (r RegionType) Valid() error {
for _, region := range Regions {
if r == RegionType(region.Value.(string)) {
return nil
}
}
return false
}

// Enum implements the stack.Enumer interface
func (Region) Enum() []interface{} {
// TODO: check whether the valid regions have metadata listed as their
// features and only show those regions, because User Data is currently
// only available in regions with metadata listed in their features.
regions := make([]interface{}, len(validRegions))

for i, region := range validRegions {
regions[i] = region
}

return regions
return fmt.Errorf("region %q does not exist", r)
}
53 changes: 25 additions & 28 deletions go/src/koding/kites/kloud/provider/google/google.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,45 +31,42 @@ func init() {
provider.Register(p)
}

// Region represents google's geographical region code.
type Region string

var regions = []Region{
"asia-east1",
"europe-west1",
"us-central1",
"us-east1",
"us-west1",
// RegionType represents google's geographical region code.
type RegionType string

var _ stack.Enumer = RegionType("")

var Regions = []stack.Enum{
{Title: "Asia West 1", Value: "asia-east1"},
{Title: "Europte West 1", Value: "europe-west1"},
{Title: "US Central 1", Value: "us-central1"},
{Title: "US East 1", Value: "us-east1"},
{Title: "US West 1", Value: "us-west1"},
}

// Enum returns all available regions for "google" provider.
func (Region) Enum() (rs []interface{}) {
for _, region := range regions {
rs = append(rs, region)
}
return rs
}
func (RegionType) Enums() []stack.Enum { return Regions }

// Valid checks if stored region code is available in GCP.
func (r Region) Valid() error {
func (r RegionType) Valid() error {
if r == "" {
return fmt.Errorf("region name is not set")
}

for _, region := range regions {
if r == region {
for _, region := range Regions {
if r == RegionType(region.Value.(string)) {
return nil
}
}

return fmt.Errorf("unknown region name: %v", r)
return fmt.Errorf("region %q does not exist", r)
}

// Cred represents jCredentialDatas.meta for "google" provider.
type Cred struct {
Credentials string `json:"credentials" bson:"credentials" hcl:"credentials" kloud:",secret"`
Project string `json:"project" bson:"project" hcl:"project"`
Region Region `json:"region" bson:"region" hcl:"region"`
Credentials string `json:"credentials" bson:"credentials" hcl:"credentials" kloud:",secret"`
Project string `json:"project" bson:"project" hcl:"project"`
Region RegionType `json:"region" bson:"region" hcl:"region"`
}

var _ stack.Validator = (*Cred)(nil)
Expand Down Expand Up @@ -139,12 +136,12 @@ func (b *Bootstrap) Valid() error {
}

type Meta struct {
Name string `json:"name" bson:"name" hcl:"name"`
Region Region `json:"region" bson:"region" hcl:"region"`
Zone string `json:"zone" bson:"zone" hcl:"zone"`
Image string `json:"image" bson:"image" hcl:"image"`
StorageSize int `json:"storage_size" bson:"storage_size" hcl:"storage_size"`
MachineType string `json:"machine_type" bson:"machine_type" hcl:"machine_type"`
Name string `json:"name" bson:"name" hcl:"name"`
Region RegionType `json:"region" bson:"region" hcl:"region"`
Zone string `json:"zone" bson:"zone" hcl:"zone"`
Image string `json:"image" bson:"image" hcl:"image"`
StorageSize int `json:"storage_size" bson:"storage_size" hcl:"storage_size"`
MachineType string `json:"machine_type" bson:"machine_type" hcl:"machine_type"`
}

var _ stack.Validator = (*Meta)(nil)
Expand Down
39 changes: 36 additions & 3 deletions go/src/koding/kites/kloud/stack/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,37 @@ type Enum struct {
Value interface{} `json:"value"`
}

// Enums is an enum list.
type Enums []Enum

// Contains gives true if enums contains the given value.
func (e Enums) Contains(value interface{}) bool {
for _, e := range e {
if e.Value == value {
return true
}
}
return false
}

// Values gives all enums' values.
func (e Enums) Values() []interface{} {
v := make([]interface{}, len(e))
for i := range e {
v[i] = e[i].Value
}
return v
}

// Titles gives all enums' titles.
func (e Enums) Titles() []string {
t := make([]string, len(e))
for i := range e {
t[i] = e[i].Title
}
return t
}

// Value represents a description of a single
// field within Bootstrap or Credential struct.
type Value struct {
Expand All @@ -56,7 +87,7 @@ type Value struct {
Label string `json:"label"`
Secret bool `json:"secret"`
ReadOnly bool `json:"readOnly"`
Values []Enum `json:"values,omitempty"`
Values Enums `json:"values,omitempty"`
}

// CredentialListRequest represents a request
Expand Down Expand Up @@ -204,8 +235,10 @@ type CredentialAddResponse struct {
func (k *Kloud) CredentialDescribe(r *kite.Request) (interface{}, error) {
var req CredentialDescribeRequest

if err := r.Args.One().Unmarshal(&req); err != nil {
return nil, err
if r.Args != nil {
if err := r.Args.One().Unmarshal(&req); err != nil {
return nil, err
}
}

// TODO: add support for reading the provider names by parsing
Expand Down
38 changes: 37 additions & 1 deletion go/src/koding/kites/kloud/stack/provider/describe.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
package provider

import (
"bytes"
"fmt"
"koding/kites/kloud/stack"
"reflect"
"strings"
"time"
"unicode"
)

var describeTags = []string{"bson", "hcl", "json"}

var durationTyp = reflect.TypeOf((*time.Duration)(nil)).Elem()

// Describe creates a schema definition for the given v value.
//
// It is used in "credential.describe" kloud method, for
Expand Down Expand Up @@ -46,7 +51,7 @@ func Describe(v interface{}) ([]stack.Value, error) {

v := stack.Value{
Name: f.Name,
Label: strings.Title(f.Name),
Label: title(f.Name),
}

switch fTyp.Kind() {
Expand All @@ -64,6 +69,10 @@ func Describe(v interface{}) ([]stack.Value, error) {
v.Type = "object" // TODO(rjeczalik): this may be not needed
}

if fTyp == durationTyp {
v.Type = "duration"
}

if enumer, ok := val.Field(i).Interface().(stack.Enumer); ok {
v.Type = "enum"
v.Values = enumer.Enums()
Expand Down Expand Up @@ -113,3 +122,30 @@ func mustDescribe(v interface{}) []stack.Value {
}
return desc
}

var titler = strings.NewReplacer("_", " ")

func title(s string) string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about:

var re = regexp.MustCompile(`(\s*[[:upper:]]+|_)`)

func title(s string) string {
	titleF := func(match string) string {
		if rs := []rune(match); unicode.IsLower(rs[len(rs)-1]) {
			// Format matches like AAAAa -> AAA Aa.
			match = string(rs[:len(rs)-2]) + " " + string(rs[len(rs)-2:])
		}
		return " " + strings.Trim(match, "_ ")
	}

	return strings.Title(strings.TrimSpace(re.ReplaceAllStringFunc(s, titleF)))
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ping. Merging for now.

I find it harder to reason as in your version there's more "magic" behind regexp functors.

However if you find a use-case that existing impl is not enough, feel encouraged to send PR with your version.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of this line. After some thought - my solution has quite similar level of complexity so lets left this as is.

if s == "" {
return ""
}

var buf bytes.Buffer

runes := []rune(strings.Title(titler.Replace(s)))
buf.WriteRune(runes[0])

for i := 1; i < len(runes)-1; i++ {
if !unicode.IsSpace(runes[i]) && !unicode.IsSpace(runes[i-1]) && unicode.IsUpper(runes[i]) && (unicode.IsLower(runes[i+1]) || unicode.IsLower(runes[i-1])) {
buf.WriteRune(' ')
}

buf.WriteRune(runes[i])
}

if len(runes) != 1 {
buf.WriteRune(runes[len(runes)-1])
}

return buf.String()
}
Loading