Skip to content

Commit

Permalink
environs: add PrecheckInstanceParams
Browse files Browse the repository at this point in the history
  • Loading branch information
axw committed Jun 2, 2017
1 parent 5d9fad9 commit 4dba11f
Show file tree
Hide file tree
Showing 31 changed files with 334 additions and 185 deletions.
6 changes: 5 additions & 1 deletion apiserver/uniter/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,11 @@ func (s *StorageAPI) removeOneStorageAttachment(id params.StorageAttachmentId, c
if err != nil {
return err
}
return s.st.RemoveStorageAttachment(storageTag, unitTag)
err = s.st.RemoveStorageAttachment(storageTag, unitTag)
if errors.IsNotFound(err) {
err = nil
}
return err
}

// AddUnitStorage validates and creates additional storage instances for units.
Expand Down
38 changes: 29 additions & 9 deletions environs/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,16 @@ type Environ interface {
// Provider returns the EnvironProvider that created this Environ.
Provider() EnvironProvider

InstancePrechecker

// InstanceTypesFetcher represents an environment that can return
// information about the available instance types.
InstanceTypesFetcher
}

// InstancePrechecker provides a means of "prechecking" instance
// arguments before recording them in state.
type InstancePrechecker interface {
// PrecheckInstance performs a preflight check on the specified
// series and constraints, ensuring that they are possibly valid for
// creating an instance in this model.
Expand All @@ -344,16 +354,26 @@ type Environ interface {
// all invalid parameters. If PrecheckInstance returns nil, it is not
// guaranteed that the constraints are valid; if a non-nil error is
// returned, then the constraints are definitely invalid.
//
// TODO(axw) find a home for state.Prechecker that isn't state and
// isn't environs, so both packages can refer to it. Maybe the
// constraints package? Can't be instance, because constraints
// import instance...
PrecheckInstance(series string, cons constraints.Value, placement string) error
PrecheckInstance(PrecheckInstanceParams) error
}

// InstanceTypesFetcher represents an environment that can return
// information about the available instance types.
InstanceTypesFetcher
// PrecheckInstanceParams contains the parameters for
// InstancePrechecker.PrecheckInstance.
type PrecheckInstanceParams struct {
// Series contains the series of the machine.
Series string

// Constraints contains the machine constraints.
Constraints constraints.Value

// Placement contains the machine placement directive, if any.
Placement string

// VolumeAttachments contains the parameters for attaching existing
// volumes to the instance. The PrecheckInstance method should not
// expect the attachment's Machine field to be set, as PrecheckInstance
// may be called before a machine ID is allocated.
VolumeAttachments []storage.VolumeAttachmentParams
}

// CreateParams contains the parameters for Environ.Create.
Expand Down
15 changes: 7 additions & 8 deletions environs/jujutest/livetests.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,13 @@ func (t *LiveTests) Destroy(c *gc.C) {
}

func (t *LiveTests) TestPrechecker(c *gc.C) {
// Providers may implement Prechecker. If they do, then they should
// return nil for empty constraints (excluding the null provider).
prechecker, ok := t.Env.(state.Prechecker)
if !ok {
return
}
const placement = ""
err := prechecker.PrecheckInstance("precise", constraints.Value{}, placement)
// All implementations of InstancePrechecker should
// return nil for empty constraints (excluding the
// manual provider).
t.PrepareOnce(c)
err := t.Env.PrecheckInstance(environs.PrecheckInstanceParams{
Series: "precise",
})
c.Assert(err, jc.ErrorIsNil)
}

Expand Down
16 changes: 7 additions & 9 deletions provider/azure/environ.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ import (
"github.com/juju/juju/provider/azure/internal/tracing"
"github.com/juju/juju/provider/azure/internal/useragent"
"github.com/juju/juju/provider/common"
"github.com/juju/juju/state"
"github.com/juju/juju/tools"
)

Expand Down Expand Up @@ -111,7 +110,6 @@ type azureEnviron struct {
}

var _ environs.Environ = (*azureEnviron)(nil)
var _ state.Prechecker = (*azureEnviron)(nil)

// newEnviron creates a new azureEnviron.
func newEnviron(
Expand Down Expand Up @@ -392,12 +390,12 @@ func (env *azureEnviron) ConstraintsValidator() (constraints.Validator, error) {
return validator, nil
}

// PrecheckInstance is defined on the state.Prechecker interface.
func (env *azureEnviron) PrecheckInstance(series string, cons constraints.Value, placement string) error {
if placement != "" {
return fmt.Errorf("unknown placement directive: %s", placement)
// PrecheckInstance is defined on the environs.InstancePrechecker interface.
func (env *azureEnviron) PrecheckInstance(args environs.PrecheckInstanceParams) error {
if args.Placement != "" {
return fmt.Errorf("unknown placement directive: %s", args.Placement)
}
if !cons.HasInstanceType() {
if !args.Constraints.HasInstanceType() {
return nil
}
// Constraint has an instance-type constraint so let's see if it is valid.
Expand All @@ -406,11 +404,11 @@ func (env *azureEnviron) PrecheckInstance(series string, cons constraints.Value,
return err
}
for _, instanceType := range instanceTypes {
if instanceType.Name == *cons.InstanceType {
if instanceType.Name == *args.Constraints.InstanceType {
return nil
}
}
return fmt.Errorf("invalid instance type %q", *cons.InstanceType)
return fmt.Errorf("invalid instance type %q", *args.Constraints.InstanceType)
}

// MaintainInstance is specified in the InstanceBroker interface.
Expand Down
3 changes: 1 addition & 2 deletions provider/cloudsigma/environ.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/juju/errors"
"github.com/juju/version"

"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/config"
"github.com/juju/juju/environs/simplestreams"
Expand Down Expand Up @@ -131,7 +130,7 @@ func (env *environ) DestroyController(controllerUUID string) error {
// all invalid parameters. If PrecheckInstance returns nil, it is not
// guaranteed that the constraints are valid; if a non-nil error is
// returned, then the constraints are definitely invalid.
func (env *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
func (env *environ) PrecheckInstance(environs.PrecheckInstanceParams) error {
return nil
}

Expand Down
2 changes: 1 addition & 1 deletion provider/cloudsigma/environ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (s *environSuite) TestBase(c *gc.C) {
c.Assert(cfg, gc.NotNil)
c.Check(cfg.Name(), gc.Equals, "testname")

c.Check(env.PrecheckInstance("", constraints.Value{}, ""), gc.IsNil)
c.Check(env.PrecheckInstance(environs.PrecheckInstanceParams{}), gc.IsNil)

hasRegion, ok := env.(simplestreams.HasRegion)
c.Check(ok, gc.Equals, true)
Expand Down
8 changes: 4 additions & 4 deletions provider/dummy/environs.go
Original file line number Diff line number Diff line change
Expand Up @@ -658,10 +658,10 @@ func (e *environ) checkBroken(method string) error {
return nil
}

// PrecheckInstance is specified in the state.Prechecker interface.
func (*environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
if placement != "" && placement != "valid" {
return fmt.Errorf("%s placement is invalid", placement)
// PrecheckInstance is specified in the environs.InstancePrechecker interface.
func (*environ) PrecheckInstance(args environs.PrecheckInstanceParams) error {
if args.Placement != "" && args.Placement != "valid" {
return fmt.Errorf("%s placement is invalid", args.Placement)
}
return nil
}
Expand Down
20 changes: 10 additions & 10 deletions provider/ec2/environ.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,14 @@ func (e *environ) parsePlacement(placement string) (*ec2Placement, error) {
return nil, fmt.Errorf("unknown placement directive: %v", placement)
}

// PrecheckInstance is defined on the state.Prechecker interface.
func (e *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
if placement != "" {
if _, err := e.parsePlacement(placement); err != nil {
// PrecheckInstance is defined on the environs.InstancePrechecker interface.
func (e *environ) PrecheckInstance(args environs.PrecheckInstanceParams) error {
if args.Placement != "" {
if _, err := e.parsePlacement(args.Placement); err != nil {
return err
}
}
if !cons.HasInstanceType() {
if !args.Constraints.HasInstanceType() {
return nil
}
// Constraint has an instance-type constraint so let's see if it is valid.
Expand All @@ -318,17 +318,17 @@ func (e *environ) PrecheckInstance(series string, cons constraints.Value, placem
return errors.Trace(err)
}
for _, itype := range instanceTypes {
if itype.Name != *cons.InstanceType {
if itype.Name != *args.Constraints.InstanceType {
continue
}
if archMatches(itype.Arches, cons.Arch) {
if archMatches(itype.Arches, args.Constraints.Arch) {
return nil
}
}
if cons.Arch == nil {
return fmt.Errorf("invalid AWS instance type %q specified", *cons.InstanceType)
if args.Constraints.Arch == nil {
return fmt.Errorf("invalid AWS instance type %q specified", *args.Constraints.InstanceType)
}
return fmt.Errorf("invalid AWS instance type %q and arch %q specified", *cons.InstanceType, *cons.Arch)
return fmt.Errorf("invalid AWS instance type %q and arch %q specified", *args.Constraints.InstanceType, *args.Constraints.Arch)
}

// MetadataLookupParams returns parameters which are used to query simplestreams metadata.
Expand Down
2 changes: 0 additions & 2 deletions provider/ec2/environ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,13 @@ import (
"github.com/juju/juju/environs/simplestreams"
"github.com/juju/juju/instance"
"github.com/juju/juju/network"
"github.com/juju/juju/state"
)

// Ensure EC2 provider supports the expected interfaces,
var (
_ environs.NetworkingEnviron = (*environ)(nil)
_ config.ConfigSchemaSource = (*environProvider)(nil)
_ simplestreams.HasRegion = (*environ)(nil)
_ state.Prechecker = (*environ)(nil)
_ instance.Distributor = (*environ)(nil)
)

Expand Down
11 changes: 6 additions & 5 deletions provider/gce/environ_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@ import (
"github.com/juju/errors"

"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
)

// PrecheckInstance verifies that the provided series and constraints
// are valid for use in creating an instance in this environment.
func (env *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
if _, err := env.parsePlacement(placement); err != nil {
func (env *environ) PrecheckInstance(args environs.PrecheckInstanceParams) error {
if _, err := env.parsePlacement(args.Placement); err != nil {
return errors.Trace(err)
}

if cons.HasInstanceType() {
if !checkInstanceType(cons) {
return errors.Errorf("invalid GCE instance type %q", *cons.InstanceType)
if args.Constraints.HasInstanceType() {
if !checkInstanceType(args.Constraints) {
return errors.Errorf("invalid GCE instance type %q", *args.Constraints.InstanceType)
}
}

Expand Down
15 changes: 7 additions & 8 deletions provider/joyent/environ.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"github.com/juju/errors"
"github.com/juju/version"

"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
"github.com/juju/juju/environs/config"
"github.com/juju/juju/environs/simplestreams"
Expand Down Expand Up @@ -57,12 +56,12 @@ func (*joyentEnviron) Provider() environs.EnvironProvider {
return providerInstance
}

// PrecheckInstance is defined on the state.Prechecker interface.
func (env *joyentEnviron) PrecheckInstance(series string, cons constraints.Value, placement string) error {
if placement != "" {
return fmt.Errorf("unknown placement directive: %s", placement)
// PrecheckInstance is defined on the environs.InstancePrechecker interface.
func (env *joyentEnviron) PrecheckInstance(args environs.PrecheckInstanceParams) error {
if args.Placement != "" {
return fmt.Errorf("unknown placement directive: %s", args.Placement)
}
if !cons.HasInstanceType() {
if !args.Constraints.HasInstanceType() {
return nil
}
// Constraint has an instance-type constraint so let's see if it is valid.
Expand All @@ -71,11 +70,11 @@ func (env *joyentEnviron) PrecheckInstance(series string, cons constraints.Value
return err
}
for _, instanceType := range instanceTypes {
if instanceType.Name == *cons.InstanceType {
if instanceType.Name == *args.Constraints.InstanceType {
return nil
}
}
return fmt.Errorf("invalid Joyent instance %q specified", *cons.InstanceType)
return fmt.Errorf("invalid Joyent instance %q specified", *args.Constraints.InstanceType)
}

func (env *joyentEnviron) SetConfig(cfg *config.Config) error {
Expand Down
9 changes: 5 additions & 4 deletions provider/lxd/environ_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ import (
"github.com/juju/utils/arch"

"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
)

// PrecheckInstance verifies that the provided series and constraints
// are valid for use in creating an instance in this environment.
func (env *environ) PrecheckInstance(series string, cons constraints.Value, placement string) error {
if _, err := env.parsePlacement(placement); err != nil {
func (env *environ) PrecheckInstance(args environs.PrecheckInstanceParams) error {
if _, err := env.parsePlacement(args.Placement); err != nil {
return errors.Trace(err)
}

if cons.HasInstanceType() {
return errors.Errorf("LXD does not support instance types (got %q)", *cons.InstanceType)
if args.Constraints.HasInstanceType() {
return errors.Errorf("LXD does not support instance types (got %q)", *args.Constraints.InstanceType)
}

return nil
Expand Down
27 changes: 7 additions & 20 deletions provider/lxd/environ_policy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
gc "gopkg.in/check.v1"

"github.com/juju/juju/constraints"
"github.com/juju/juju/environs"
"github.com/juju/juju/provider/lxd"
)

Expand All @@ -23,35 +24,23 @@ type environPolSuite struct {

var _ = gc.Suite(&environPolSuite{})

func (s *environPolSuite) TestPrecheckInstanceOkay(c *gc.C) {
cons := constraints.Value{}
placement := ""
err := s.Env.PrecheckInstance(series.LatestLts(), cons, placement)

c.Check(err, jc.ErrorIsNil)
}

func (s *environPolSuite) TestPrecheckInstanceAPI(c *gc.C) {
cons := constraints.Value{}
placement := ""
err := s.Env.PrecheckInstance(series.LatestLts(), cons, placement)
func (s *environPolSuite) TestPrecheckInstanceDefaults(c *gc.C) {
err := s.Env.PrecheckInstance(environs.PrecheckInstanceParams{Series: series.LatestLts()})
c.Assert(err, jc.ErrorIsNil)

s.CheckNoAPI(c)
}

func (s *environPolSuite) TestPrecheckInstanceHasInstanceType(c *gc.C) {
cons := constraints.MustParse("instance-type=some-instance-type")
placement := ""
err := s.Env.PrecheckInstance(series.LatestLts(), cons, placement)
err := s.Env.PrecheckInstance(environs.PrecheckInstanceParams{Series: series.LatestLts(), Constraints: cons})

c.Check(err, gc.ErrorMatches, `LXD does not support instance types.*`)
}

func (s *environPolSuite) TestPrecheckInstanceDiskSize(c *gc.C) {
cons := constraints.MustParse("root-disk=1G")
placement := ""
err := s.Env.PrecheckInstance(series.LatestLts(), cons, placement)
err := s.Env.PrecheckInstance(environs.PrecheckInstanceParams{Series: series.LatestLts(), Constraints: cons})

c.Check(err, jc.ErrorIsNil)
}
Expand All @@ -60,16 +49,14 @@ func (s *environPolSuite) TestPrecheckInstanceUnsupportedArch(c *gc.C) {
s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 })

cons := constraints.MustParse("arch=i386")
placement := ""
err := s.Env.PrecheckInstance(series.LatestLts(), cons, placement)
err := s.Env.PrecheckInstance(environs.PrecheckInstanceParams{Series: series.LatestLts(), Constraints: cons})

c.Check(err, jc.ErrorIsNil)
}

func (s *environPolSuite) TestPrecheckInstanceAvailZone(c *gc.C) {
cons := constraints.Value{}
placement := "zone=a-zone"
err := s.Env.PrecheckInstance(series.LatestLts(), cons, placement)
err := s.Env.PrecheckInstance(environs.PrecheckInstanceParams{Series: series.LatestLts(), Placement: placement})

c.Check(err, gc.ErrorMatches, `unknown placement directive: .*`)
}
Expand Down
Loading

0 comments on commit 4dba11f

Please sign in to comment.