Skip to content

Commit

Permalink
provider/gce: Duplicate Operation{Waiter,Error}
Browse files Browse the repository at this point in the history
Type safety means that `HttpsHealthCheck`, which uses the `compute/v0.beta`
library instead of `compute/v1`, can't re-use `OperationWaiter` and
`OperationError` because all of the objects passed in are considered to be
of a different type. So we have to duplicate them to accept the correct
types.

I did attempt to use empty interfaces and type assertions but I still needed
to duplicate the entire `switch` statement in `RefreshFunc()` and it looked
even more complicated and inefficient than this.

Although somewhat unconventional I have duplicated the structs and methods
one-by-one rather than adding everything to the end of the file. This is in
the hope that it makes it more obvious that you need to update both if you
make a change.

If anyone finds a nicer way to do this in the future then I would be more
than happy for it to be refactored.
  • Loading branch information
dcarley committed Jul 14, 2015
1 parent 3c83399 commit ff37814
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 6 deletions.
60 changes: 60 additions & 0 deletions builtin/providers/google/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"fmt"

computeBeta "google.golang.org/api/compute/v0.beta"
"google.golang.org/api/compute/v1"

"github.com/hashicorp/terraform/helper/resource"
Expand All @@ -29,6 +30,15 @@ type OperationWaiter struct {
Type OperationWaitType
}

type OperationWaiterBeta struct {
Service *computeBeta.Service
Op *computeBeta.Operation
Project string
Region string
Zone string
Type OperationWaitType
}

func (w *OperationWaiter) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *compute.Operation
Expand Down Expand Up @@ -57,6 +67,34 @@ func (w *OperationWaiter) RefreshFunc() resource.StateRefreshFunc {
}
}

func (w *OperationWaiterBeta) RefreshFunc() resource.StateRefreshFunc {
return func() (interface{}, string, error) {
var op *computeBeta.Operation
var err error

switch w.Type {
case OperationWaitGlobal:
op, err = w.Service.GlobalOperations.Get(
w.Project, w.Op.Name).Do()
case OperationWaitRegion:
op, err = w.Service.RegionOperations.Get(
w.Project, w.Region, w.Op.Name).Do()
case OperationWaitZone:
op, err = w.Service.ZoneOperations.Get(
w.Project, w.Zone, w.Op.Name).Do()
default:
return nil, "bad-type", fmt.Errorf(
"Invalid wait type: %#v", w.Type)
}

if err != nil {
return nil, "", err
}

return op, op.Status, nil
}
}

func (w *OperationWaiter) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Expand All @@ -65,10 +103,22 @@ func (w *OperationWaiter) Conf() *resource.StateChangeConf {
}
}

func (w *OperationWaiterBeta) Conf() *resource.StateChangeConf {
return &resource.StateChangeConf{
Pending: []string{"PENDING", "RUNNING"},
Target: "DONE",
Refresh: w.RefreshFunc(),
}
}

// OperationError wraps compute.OperationError and implements the
// error interface so it can be returned.
type OperationError compute.OperationError

// OperationErrorBeta wraps computeBeta.OperationError and implements the
// error interface so it can be returned.
type OperationErrorBeta computeBeta.OperationError

func (e OperationError) Error() string {
var buf bytes.Buffer

Expand All @@ -78,3 +128,13 @@ func (e OperationError) Error() string {

return buf.String()
}

func (e OperationErrorBeta) Error() string {
var buf bytes.Buffer

for _, err := range e.Errors {
buf.WriteString(err.Message + "\n")
}

return buf.String()
}
12 changes: 6 additions & 6 deletions builtin/providers/google/resource_compute_https_health_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func resourceComputeHttpsHealthCheckCreate(d *schema.ResourceData, meta interfac
d.SetId(hchk.Name)

// Wait for the operation to complete
w := &OperationWaiter{
w := &OperationWaiterBeta{
Service: config.clientComputeBeta,
Op: op,
Project: config.Project,
Expand All @@ -141,7 +141,7 @@ func resourceComputeHttpsHealthCheckCreate(d *schema.ResourceData, meta interfac
d.SetId("")

// Return the error
return OperationError(*op.Error)
return OperationErrorBeta(*op.Error)
}

return resourceComputeHttpsHealthCheckRead(d, meta)
Expand Down Expand Up @@ -191,7 +191,7 @@ func resourceComputeHttpsHealthCheckUpdate(d *schema.ResourceData, meta interfac
d.SetId(hchk.Name)

// Wait for the operation to complete
w := &OperationWaiter{
w := &OperationWaiterBeta{
Service: config.clientComputeBeta,
Op: op,
Project: config.Project,
Expand All @@ -210,7 +210,7 @@ func resourceComputeHttpsHealthCheckUpdate(d *schema.ResourceData, meta interfac
d.SetId("")

// Return the error
return OperationError(*op.Error)
return OperationErrorBeta(*op.Error)
}

return resourceComputeHttpsHealthCheckRead(d, meta)
Expand Down Expand Up @@ -255,7 +255,7 @@ func resourceComputeHttpsHealthCheckDelete(d *schema.ResourceData, meta interfac
}

// Wait for the operation to complete
w := &OperationWaiter{
w := &OperationWaiterBeta{
Service: config.clientComputeBeta,
Op: op,
Project: config.Project,
Expand All @@ -271,7 +271,7 @@ func resourceComputeHttpsHealthCheckDelete(d *schema.ResourceData, meta interfac
op = opRaw.(*compute.Operation)
if op.Error != nil {
// Return the error
return OperationError(*op.Error)
return OperationErrorBeta(*op.Error)
}

d.SetId("")
Expand Down

1 comment on commit ff37814

@sparkprime
Copy link
Contributor

Choose a reason for hiding this comment

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

I think is OK. Go is notoriously bad at polymorphism so code duplication is yes. This is also a temporary situation until it's out of beta.

Please sign in to comment.