mirror of https://github.com/kubernetes/kops.git
Merge pull request #3707 from andrewsykim/droplet
Automatic merge from submit-queue. Implement DigitalOcean Droplet FI Task Implements cloudup fi tasks for DigitalOcean droplets. It makes a few assumptions to reduce the size of this PR, those will be addressed in future PRs. Also does some cleanup in the DigitalOcean `dns` package.
This commit is contained in:
commit
a4d6895472
|
|
@ -444,6 +444,28 @@ func RunCreateCluster(f *util.Factory, out io.Writer, c *CreateClusterOptions) e
|
|||
}
|
||||
zoneToSubnetMap[zoneName] = subnet
|
||||
}
|
||||
} else if api.CloudProviderID(cluster.Spec.CloudProvider) == api.CloudProviderDO {
|
||||
if len(c.Zones) > 1 {
|
||||
return fmt.Errorf("digitalocean cloud provider currently only supports 1 region, expect multi-region support when digitalocean support is in beta")
|
||||
}
|
||||
|
||||
// For DO we just pass in the region for --zones
|
||||
region := c.Zones[0]
|
||||
subnet := model.FindSubnet(cluster, region)
|
||||
|
||||
// for DO, subnets are just regions
|
||||
subnetName := region
|
||||
|
||||
if subnet == nil {
|
||||
subnet = &api.ClusterSubnetSpec{
|
||||
Name: subnetName,
|
||||
// region and zone are the same for DO
|
||||
Region: region,
|
||||
Zone: region,
|
||||
}
|
||||
cluster.Spec.Subnets = append(cluster.Spec.Subnets, *subnet)
|
||||
}
|
||||
zoneToSubnetMap[region] = subnet
|
||||
} else {
|
||||
for _, zoneName := range allZones.List() {
|
||||
// We create default subnets named the same as the zones
|
||||
|
|
|
|||
|
|
@ -75,6 +75,7 @@ k8s.io/kops/pkg/model
|
|||
k8s.io/kops/pkg/model/awsmodel
|
||||
k8s.io/kops/pkg/model/components
|
||||
k8s.io/kops/pkg/model/defaults
|
||||
k8s.io/kops/pkg/model/domodel
|
||||
k8s.io/kops/pkg/model/gcemodel
|
||||
k8s.io/kops/pkg/model/iam
|
||||
k8s.io/kops/pkg/model/resources
|
||||
|
|
|
|||
|
|
@ -80,9 +80,6 @@ func ValidateCluster(c *kops.Cluster, strict bool) *field.Error {
|
|||
switch kops.CloudProviderID(c.Spec.CloudProvider) {
|
||||
case kops.CloudProviderBareMetal:
|
||||
requiresSubnets = false
|
||||
if c.Spec.NetworkCIDR != "" {
|
||||
return field.Invalid(fieldSpec.Child("NetworkCIDR"), c.Spec.NetworkCIDR, "NetworkCIDR should not be set on bare metal")
|
||||
}
|
||||
requiresNetworkCIDR = false
|
||||
if c.Spec.NetworkCIDR != "" {
|
||||
return field.Invalid(fieldSpec.Child("NetworkCIDR"), c.Spec.NetworkCIDR, "NetworkCIDR should not be set on bare metal")
|
||||
|
|
@ -96,6 +93,12 @@ func ValidateCluster(c *kops.Cluster, strict bool) *field.Error {
|
|||
requiresSubnetCIDR = false
|
||||
|
||||
case kops.CloudProviderDO:
|
||||
requiresSubnets = false
|
||||
requiresSubnetCIDR = false
|
||||
requiresNetworkCIDR = false
|
||||
if c.Spec.NetworkCIDR != "" {
|
||||
return field.Invalid(fieldSpec.Child("NetworkCIDR"), c.Spec.NetworkCIDR, "NetworkCIDR should not be set on DigitalOcean")
|
||||
}
|
||||
case kops.CloudProviderAWS:
|
||||
case kops.CloudProviderVSphere:
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package domodel
|
||||
|
||||
import "k8s.io/kops/pkg/model"
|
||||
|
||||
// DigitalOcean Model Context
|
||||
type DOModelContext struct {
|
||||
*model.KopsModelContext
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package domodel
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/kops/pkg/model"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/dotasks"
|
||||
)
|
||||
|
||||
// DropletBuilder configures droplets for the cluster
|
||||
type DropletBuilder struct {
|
||||
*DOModelContext
|
||||
|
||||
BootstrapScript *model.BootstrapScript
|
||||
Lifecycle *fi.Lifecycle
|
||||
}
|
||||
|
||||
var _ fi.ModelBuilder = &DropletBuilder{}
|
||||
|
||||
func (d *DropletBuilder) Build(c *fi.ModelBuilderContext) error {
|
||||
sshKeyName, err := d.SSHKeyName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
splitSSHKeyName := strings.Split(sshKeyName, "-")
|
||||
sshKeyFingerPrint := splitSSHKeyName[len(splitSSHKeyName)-1]
|
||||
|
||||
// replace "." with "-" since DO API does not accept "."
|
||||
clusterTag := "KubernetesCluster:" + strings.Replace(d.ClusterName(), ".", "-", -1)
|
||||
|
||||
for _, ig := range d.InstanceGroups {
|
||||
name := d.AutoscalingGroupName(ig)
|
||||
|
||||
var droplet dotasks.Droplet
|
||||
droplet.Name = fi.String(name)
|
||||
// during alpha support we only allow 1 region
|
||||
// validation for only 1 region is done at this point
|
||||
droplet.Region = fi.String(d.Cluster.Spec.Subnets[0].Region)
|
||||
droplet.Size = fi.String(ig.Spec.MachineType)
|
||||
droplet.Image = fi.String(ig.Spec.Image)
|
||||
droplet.SSHKey = fi.String(sshKeyFingerPrint)
|
||||
droplet.Tags = []string{clusterTag}
|
||||
|
||||
userData, err := d.BootstrapScript.ResourceNodeUp(ig, &d.Cluster.Spec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
droplet.UserData = userData
|
||||
|
||||
c.AddTask(&droplet)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -114,6 +114,10 @@ func (c *Cloud) Volumes() godo.StorageService {
|
|||
return c.Client.Storage
|
||||
}
|
||||
|
||||
func (c *Cloud) Droplets() godo.DropletsService {
|
||||
return c.Client.Droplets
|
||||
}
|
||||
|
||||
// FindVPCInfo is not implemented, it's only here to satisfy the fi.Cloud interface
|
||||
func (c *Cloud) FindVPCInfo(id string) (*fi.VPCInfo, error) {
|
||||
return nil, errors.New("not implemented")
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ package dns
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
"github.com/digitalocean/godo/context"
|
||||
|
|
@ -311,7 +309,7 @@ func (r *resourceRecordChangeset) Apply() error {
|
|||
|
||||
err := deleteRecord(r.client, r.zone.Name(), desiredRecord.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to delete record: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -321,7 +319,7 @@ func (r *resourceRecordChangeset) Apply() error {
|
|||
if len(r.upserts) > 0 {
|
||||
records, err := getRecords(r.client, r.zone.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to get records: %v", err)
|
||||
}
|
||||
|
||||
for _, record := range r.upserts {
|
||||
|
|
@ -346,7 +344,7 @@ func (r *resourceRecordChangeset) Apply() error {
|
|||
}
|
||||
err := editRecord(r.client, r.zone.Name(), desiredRecord.ID, domainEditRequest)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to edit record: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -374,112 +372,70 @@ func (r *resourceRecordChangeset) ResourceRecordSets() dnsprovider.ResourceRecor
|
|||
// listDomains returns a list of godo.Domain
|
||||
func listDomains(c *godo.Client) ([]godo.Domain, error) {
|
||||
// TODO (andrewsykim): pagination in ListOptions
|
||||
domains, resp, err := c.Domains.List(context.TODO(), &godo.ListOptions{})
|
||||
domains, _, err := c.Domains.List(context.TODO(), &godo.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list domains: %v", err)
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domains, err
|
||||
}
|
||||
|
||||
// createDomain creates a domain provided godo.DomainCreateRequest
|
||||
func createDomain(c *godo.Client, createRequest *godo.DomainCreateRequest) (*godo.Domain, error) {
|
||||
domain, resp, err := c.Domains.Create(context.TODO(), createRequest)
|
||||
domain, _, err := c.Domains.Create(context.TODO(), createRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
// deleteDomain deletes a domain given its name
|
||||
func deleteDomain(c *godo.Client, name string) error {
|
||||
resp, err := c.Domains.Delete(context.TODO(), name)
|
||||
_, err := c.Domains.Delete(context.TODO(), name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getRecords returns a list of godo.DomainRecord given a zone name
|
||||
func getRecords(c *godo.Client, zoneName string) ([]godo.DomainRecord, error) {
|
||||
records, resp, err := c.Domains.Records(context.TODO(), zoneName, &godo.ListOptions{})
|
||||
records, _, err := c.Domains.Records(context.TODO(), zoneName, &godo.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
||||
// createRecord creates a record given an associated zone and a godo.DomainRecordEditRequest
|
||||
func createRecord(c *godo.Client, zoneName string, createRequest *godo.DomainRecordEditRequest) error {
|
||||
_, resp, err := c.Domains.CreateRecord(context.TODO(), zoneName, createRequest)
|
||||
_, _, err := c.Domains.CreateRecord(context.TODO(), zoneName, createRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying changeset: %v", err)
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// editRecord edits a record given an associated ozone and a godo.DomainRecordEditRequest
|
||||
func editRecord(c *godo.Client, zoneName string, recordID int, editRequest *godo.DomainRecordEditRequest) error {
|
||||
_, resp, err := c.Domains.EditRecord(context.TODO(), zoneName, recordID, editRequest)
|
||||
_, _, err := c.Domains.EditRecord(context.TODO(), zoneName, recordID, editRequest)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying changeset: %v", err)
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// deleteRecord deletes a record given an associated zone and a record ID
|
||||
func deleteRecord(c *godo.Client, zoneName string, recordID int) error {
|
||||
resp, err := c.Domains.DeleteRecord(context.TODO(), zoneName, recordID)
|
||||
_, err := c.Domains.DeleteRecord(context.TODO(), zoneName, recordID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error applying changeset: %v", err)
|
||||
}
|
||||
|
||||
if err = handleResponse(resp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleResponse(resp *godo.Response) error {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
respData, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading response body: %v", err)
|
||||
}
|
||||
|
||||
return fmt.Errorf("received non 200 status code: %d from api: %v",
|
||||
resp.StatusCode, string(respData))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,11 +91,7 @@ func TestZonesList(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
resp := &godo.Response{
|
||||
Response: &http.Response{},
|
||||
}
|
||||
resp.StatusCode = http.StatusOK
|
||||
return domains, resp, nil
|
||||
return domains, nil, nil
|
||||
}
|
||||
client.Domains = fake
|
||||
|
||||
|
|
@ -122,12 +118,7 @@ func TestZonesList(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
resp := &godo.Response{
|
||||
Response: &http.Response{},
|
||||
}
|
||||
resp.StatusCode = http.StatusInternalServerError
|
||||
resp.Body = ioutil.NopCloser(bytes.NewBufferString("error!"))
|
||||
return domains, resp, nil
|
||||
return domains, nil, errors.New("internal error!")
|
||||
}
|
||||
client.Domains = fake
|
||||
|
||||
|
|
@ -191,13 +182,8 @@ func TestAdd(t *testing.T) {
|
|||
// bad status code
|
||||
fake.createFunc = func(ctx context.Context, domainCreateRequest *godo.DomainCreateRequest) (*godo.Domain, *godo.Response, error) {
|
||||
domain := &godo.Domain{Name: domainCreateRequest.Name}
|
||||
resp := &godo.Response{
|
||||
Response: &http.Response{},
|
||||
}
|
||||
resp.StatusCode = http.StatusInternalServerError
|
||||
resp.Body = ioutil.NopCloser(bytes.NewBufferString("error!"))
|
||||
|
||||
return domain, resp, nil
|
||||
return domain, nil, errors.New("bad response!")
|
||||
}
|
||||
client.Domains = fake
|
||||
|
||||
|
|
@ -218,11 +204,8 @@ func TestAdd(t *testing.T) {
|
|||
// godo returns error
|
||||
fake.createFunc = func(ctx context.Context, domainCreateRequest *godo.DomainCreateRequest) (*godo.Domain, *godo.Response, error) {
|
||||
domain := &godo.Domain{Name: domainCreateRequest.Name}
|
||||
resp := &godo.Response{
|
||||
Response: &http.Response{},
|
||||
}
|
||||
|
||||
return domain, resp, errors.New("error!")
|
||||
return domain, nil, errors.New("error!")
|
||||
}
|
||||
client.Domains = fake
|
||||
|
||||
|
|
@ -247,11 +230,7 @@ func TestRemove(t *testing.T) {
|
|||
|
||||
// happy path
|
||||
fake.deleteFunc = func(ctx context.Context, name string) (*godo.Response, error) {
|
||||
resp := &godo.Response{
|
||||
Response: &http.Response{},
|
||||
}
|
||||
resp.StatusCode = http.StatusOK
|
||||
return resp, nil
|
||||
return nil, nil
|
||||
}
|
||||
client.Domains = fake
|
||||
|
||||
|
|
@ -266,12 +245,7 @@ func TestRemove(t *testing.T) {
|
|||
|
||||
// bad status code
|
||||
fake.deleteFunc = func(ctx context.Context, name string) (*godo.Response, error) {
|
||||
resp := &godo.Response{
|
||||
Response: &http.Response{},
|
||||
}
|
||||
resp.StatusCode = http.StatusInternalServerError
|
||||
resp.Body = ioutil.NopCloser(bytes.NewBufferString("error!"))
|
||||
return resp, nil
|
||||
return nil, errors.New("bad response!")
|
||||
}
|
||||
client.Domains = fake
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import (
|
|||
"k8s.io/kops/pkg/model"
|
||||
"k8s.io/kops/pkg/model/awsmodel"
|
||||
"k8s.io/kops/pkg/model/components"
|
||||
"k8s.io/kops/pkg/model/domodel"
|
||||
"k8s.io/kops/pkg/model/gcemodel"
|
||||
"k8s.io/kops/pkg/model/vspheremodel"
|
||||
"k8s.io/kops/pkg/resources/digitalocean"
|
||||
|
|
@ -351,8 +352,11 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
return fmt.Errorf("DigitalOcean support is currently (very) alpha and is feature-gated. export KOPS_FEATURE_FLAGS=AlphaAllowDO to enable it")
|
||||
}
|
||||
|
||||
modelContext.SSHPublicKeys = sshPublicKeys
|
||||
|
||||
l.AddTypes(map[string]interface{}{
|
||||
"volume": &dotasks.Volume{},
|
||||
"volume": &dotasks.Volume{},
|
||||
"droplet": &dotasks.Droplet{},
|
||||
})
|
||||
}
|
||||
case kops.CloudProviderAWS:
|
||||
|
|
@ -686,7 +690,15 @@ func (c *ApplyClusterCmd) Run() error {
|
|||
Lifecycle: clusterLifecycle,
|
||||
})
|
||||
case kops.CloudProviderDO:
|
||||
// DigitalOcean tasks will go here
|
||||
doModelContext := &domodel.DOModelContext{
|
||||
KopsModelContext: modelContext,
|
||||
}
|
||||
|
||||
l.Builders = append(l.Builders, &domodel.DropletBuilder{
|
||||
DOModelContext: doModelContext,
|
||||
BootstrapScript: bootstrapScriptBuilder,
|
||||
Lifecycle: clusterLifecycle,
|
||||
})
|
||||
case kops.CloudProviderGCE:
|
||||
{
|
||||
gceModelContext := &gcemodel.GCEModelContext{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package dotasks
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"github.com/digitalocean/godo"
|
||||
|
||||
"k8s.io/kops/pkg/resources/digitalocean"
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
"k8s.io/kops/upup/pkg/fi/cloudup/do"
|
||||
_ "k8s.io/kops/upup/pkg/fi/cloudup/terraform"
|
||||
)
|
||||
|
||||
//go:generate fitask -type=Droplet
|
||||
type Droplet struct {
|
||||
Name *string
|
||||
ID *string
|
||||
Lifecycle *fi.Lifecycle
|
||||
|
||||
Region *string
|
||||
Size *string
|
||||
Image *string
|
||||
SSHKey *string
|
||||
Tags []string
|
||||
UserData *fi.ResourceHolder
|
||||
}
|
||||
|
||||
var _ fi.CompareWithID = &Droplet{}
|
||||
|
||||
func (d *Droplet) CompareWithID() *string {
|
||||
return d.ID
|
||||
}
|
||||
|
||||
func (d *Droplet) Find(c *fi.Context) (*Droplet, error) {
|
||||
cloud := c.Cloud.(*digitalocean.Cloud)
|
||||
dropletService := cloud.Droplets()
|
||||
|
||||
droplets, _, err := dropletService.List(context.TODO(), &godo.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, droplet := range droplets {
|
||||
if droplet.Name == fi.StringValue(d.Name) {
|
||||
return &Droplet{
|
||||
Name: fi.String(droplet.Name),
|
||||
ID: fi.String(strconv.Itoa(droplet.ID)),
|
||||
Region: fi.String(droplet.Region.Slug),
|
||||
Size: fi.String(droplet.Size.Slug),
|
||||
Image: fi.String(droplet.Image.Slug),
|
||||
Lifecycle: d.Lifecycle,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (d *Droplet) Run(c *fi.Context) error {
|
||||
return fi.DefaultDeltaRunMethod(d, c)
|
||||
}
|
||||
|
||||
func (_ *Droplet) RenderDO(t *do.DOAPITarget, a, e, changes *Droplet) error {
|
||||
if a != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
userData, err := e.UserData.AsString()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dropletService := t.Cloud.Droplets()
|
||||
_, _, err = dropletService.Create(context.TODO(), &godo.DropletCreateRequest{
|
||||
Name: fi.StringValue(e.Name),
|
||||
Region: fi.StringValue(e.Region),
|
||||
Size: fi.StringValue(e.Size),
|
||||
Image: godo.DropletCreateImage{Slug: fi.StringValue(e.Image)},
|
||||
PrivateNetworking: true,
|
||||
Tags: e.Tags,
|
||||
UserData: userData,
|
||||
SSHKeys: []godo.DropletCreateSSHKey{{Fingerprint: fi.StringValue(e.SSHKey)}},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (_ *Droplet) CheckChanges(a, e, changes *Droplet) error {
|
||||
if a != nil {
|
||||
if changes.Name != nil {
|
||||
return fi.CannotChangeField("Name")
|
||||
}
|
||||
if changes.ID != nil {
|
||||
return fi.CannotChangeField("ID")
|
||||
}
|
||||
if changes.Region != nil {
|
||||
return fi.CannotChangeField("Region")
|
||||
}
|
||||
if changes.Size != nil {
|
||||
return fi.CannotChangeField("Size")
|
||||
}
|
||||
if changes.Image != nil {
|
||||
return fi.CannotChangeField("Image")
|
||||
}
|
||||
} else {
|
||||
if e.Name == nil {
|
||||
return fi.RequiredField("Name")
|
||||
}
|
||||
if e.Region == nil {
|
||||
return fi.RequiredField("Region")
|
||||
}
|
||||
if e.Size == nil {
|
||||
return fi.RequiredField("Size")
|
||||
}
|
||||
if e.Image == nil {
|
||||
return fi.RequiredField("Image")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by ""fitask" -type=Droplet"; DO NOT EDIT
|
||||
|
||||
package dotasks
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"k8s.io/kops/upup/pkg/fi"
|
||||
)
|
||||
|
||||
// Droplet
|
||||
|
||||
// JSON marshalling boilerplate
|
||||
type realDroplet Droplet
|
||||
|
||||
// UnmarshalJSON implements conversion to JSON, supporitng an alternate specification of the object as a string
|
||||
func (o *Droplet) UnmarshalJSON(data []byte) error {
|
||||
var jsonName string
|
||||
if err := json.Unmarshal(data, &jsonName); err == nil {
|
||||
o.Name = &jsonName
|
||||
return nil
|
||||
}
|
||||
|
||||
var r realDroplet
|
||||
if err := json.Unmarshal(data, &r); err != nil {
|
||||
return err
|
||||
}
|
||||
*o = Droplet(r)
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ fi.HasLifecycle = &Droplet{}
|
||||
|
||||
// GetLifecycle returns the Lifecycle of the object, implementing fi.HasLifecycle
|
||||
func (o *Droplet) GetLifecycle() *fi.Lifecycle {
|
||||
return o.Lifecycle
|
||||
}
|
||||
|
||||
var _ fi.HasName = &Droplet{}
|
||||
|
||||
// GetName returns the Name of the object, implementing fi.HasName
|
||||
func (o *Droplet) GetName() *string {
|
||||
return o.Name
|
||||
}
|
||||
|
||||
// SetName sets the Name of the object, implementing fi.SetName
|
||||
func (o *Droplet) SetName(name string) {
|
||||
o.Name = &name
|
||||
}
|
||||
|
||||
// String is the stringer function for the task, producing readable output using fi.TaskAsString
|
||||
func (o *Droplet) String() string {
|
||||
return fi.TaskAsString(o)
|
||||
}
|
||||
Loading…
Reference in New Issue