コード例 #1
0
ファイル: controller.go プロジェクト: voidspace/gomaasapi
func parseAllocateConstraintsResponse(source interface{}, machine *machine) (ConstraintMatches, error) {
	var empty ConstraintMatches
	matchFields := schema.Fields{
		"storage":    schema.StringMap(schema.ForceInt()),
		"interfaces": schema.StringMap(schema.ForceInt()),
	}
	matchDefaults := schema.Defaults{
		"storage":    schema.Omit,
		"interfaces": schema.Omit,
	}
	fields := schema.Fields{
		"constraints_by_type": schema.FieldMap(matchFields, matchDefaults),
	}
	checker := schema.FieldMap(fields, nil) // no defaults
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return empty, WrapWithDeserializationError(err, "allocation constraints response schema check failed")
	}
	valid := coerced.(map[string]interface{})
	constraintsMap := valid["constraints_by_type"].(map[string]interface{})
	result := ConstraintMatches{
		Interfaces: make(map[string]Interface),
		Storage:    make(map[string]BlockDevice),
	}

	if interfaceMatches, found := constraintsMap["interfaces"]; found {
		for label, value := range interfaceMatches.(map[string]interface{}) {
			id := value.(int)
			iface := machine.Interface(id)
			if iface == nil {
				return empty, NewDeserializationError("constraint match interface %q: %d does not match an interface for the machine", label, id)
			}
			result.Interfaces[label] = iface
		}
	}

	if storageMatches, found := constraintsMap["storage"]; found {
		for label, value := range storageMatches.(map[string]interface{}) {
			id := value.(int)
			blockDevice := machine.PhysicalBlockDevice(id)
			if blockDevice == nil {
				return empty, NewDeserializationError("constraint match storage %q: %d does not match a physical block device for the machine", label, id)
			}
			result.Storage[label] = blockDevice
		}
	}
	return result, nil
}
コード例 #2
0
ファイル: bootresource.go プロジェクト: voidspace/gomaasapi
func bootResource_2_0(source map[string]interface{}) (*bootResource, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),
		"id":           schema.ForceInt(),
		"name":         schema.String(),
		"type":         schema.String(),
		"architecture": schema.String(),
		"subarches":    schema.String(),
		"kflavor":      schema.String(),
	}
	checker := schema.FieldMap(fields, nil) // no defaults
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, WrapWithDeserializationError(err, "boot resource 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	result := &bootResource{
		resourceURI:  valid["resource_uri"].(string),
		id:           valid["id"].(int),
		name:         valid["name"].(string),
		type_:        valid["type"].(string),
		architecture: valid["architecture"].(string),
		subArches:    valid["subarches"].(string),
		kernelFlavor: valid["kflavor"].(string),
	}
	return result, nil
}
コード例 #3
0
ファイル: space.go プロジェクト: voidspace/gomaasapi
func space_2_0(source map[string]interface{}) (*space, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),
		"id":           schema.ForceInt(),
		"name":         schema.String(),
		"subnets":      schema.List(schema.StringMap(schema.Any())),
	}
	checker := schema.FieldMap(fields, nil) // no defaults
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "space 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	subnets, err := readSubnetList(valid["subnets"].([]interface{}), subnet_2_0)
	if err != nil {
		return nil, errors.Trace(err)
	}

	result := &space{
		resourceURI: valid["resource_uri"].(string),
		id:          valid["id"].(int),
		name:        valid["name"].(string),
		subnets:     subnets,
	}
	return result, nil
}
コード例 #4
0
ファイル: vlan.go プロジェクト: voidspace/gomaasapi
func vlan_2_0(source map[string]interface{}) (*vlan, error) {
	fields := schema.Fields{
		"id":           schema.ForceInt(),
		"resource_uri": schema.String(),
		"name":         schema.OneOf(schema.Nil(""), schema.String()),
		"fabric":       schema.String(),
		"vid":          schema.ForceInt(),
		"mtu":          schema.ForceInt(),
		"dhcp_on":      schema.Bool(),
		// racks are not always set.
		"primary_rack":   schema.OneOf(schema.Nil(""), schema.String()),
		"secondary_rack": schema.OneOf(schema.Nil(""), schema.String()),
	}
	checker := schema.FieldMap(fields, nil)
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "vlan 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	// Since the primary and secondary racks are optional, we use the two
	// part cast assignment. If the case fails, then we get the default value
	// we care about, which is the empty string.
	primary_rack, _ := valid["primary_rack"].(string)
	secondary_rack, _ := valid["secondary_rack"].(string)
	name, _ := valid["name"].(string)

	result := &vlan{
		resourceURI:   valid["resource_uri"].(string),
		id:            valid["id"].(int),
		name:          name,
		fabric:        valid["fabric"].(string),
		vid:           valid["vid"].(int),
		mtu:           valid["mtu"].(int),
		dhcp:          valid["dhcp_on"].(bool),
		primaryRack:   primary_rack,
		secondaryRack: secondary_rack,
	}
	return result, nil
}
コード例 #5
0
ファイル: blockdevice.go プロジェクト: voidspace/gomaasapi
func blockdevice_2_0(source map[string]interface{}) (*blockdevice, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),

		"id":       schema.ForceInt(),
		"name":     schema.String(),
		"model":    schema.String(),
		"path":     schema.String(),
		"used_for": schema.String(),
		"tags":     schema.List(schema.String()),

		"block_size": schema.ForceUint(),
		"used_size":  schema.ForceUint(),
		"size":       schema.ForceUint(),

		"partitions": schema.List(schema.StringMap(schema.Any())),
	}
	checker := schema.FieldMap(fields, nil)
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, WrapWithDeserializationError(err, "blockdevice 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	partitions, err := readPartitionList(valid["partitions"].([]interface{}), partition_2_0)
	if err != nil {
		return nil, errors.Trace(err)
	}

	result := &blockdevice{
		resourceURI: valid["resource_uri"].(string),

		id:      valid["id"].(int),
		name:    valid["name"].(string),
		model:   valid["model"].(string),
		path:    valid["path"].(string),
		usedFor: valid["used_for"].(string),
		tags:    convertToStringSlice(valid["tags"]),

		blockSize: valid["block_size"].(uint64),
		usedSize:  valid["used_size"].(uint64),
		size:      valid["size"].(uint64),

		partitions: partitions,
	}
	return result, nil
}
コード例 #6
0
ファイル: schema_test.go プロジェクト: howbazaar/schema
func (s *S) TestForceInt(c *gc.C) {
	s.sch = schema.ForceInt()

	out, err := s.sch.Coerce(42, aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	out, err = s.sch.Coerce("42", aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	out, err = s.sch.Coerce("42.66", aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	out, err = s.sch.Coerce(int8(42), aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	out, err = s.sch.Coerce(float32(42), aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	out, err = s.sch.Coerce(float64(42), aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	out, err = s.sch.Coerce(42.66, aPath)
	c.Assert(err, gc.IsNil)
	c.Assert(out, gc.Equals, int(42))

	// If an out of range value is provided, that value is truncated,
	// generating unexpected results, but no error is raised.
	out, err = s.sch.Coerce(float64(math.MaxInt64+1), aPath)
	c.Assert(err, gc.IsNil)

	out, err = s.sch.Coerce(true, aPath)
	c.Assert(out, gc.IsNil)
	c.Assert(err, gc.ErrorMatches, `<path>: expected number, got bool\(true\)`)

	out, err = s.sch.Coerce(nil, aPath)
	c.Assert(out, gc.IsNil)
	c.Assert(err, gc.ErrorMatches, "<path>: expected number, got nothing")
}
コード例 #7
0
ファイル: subnet.go プロジェクト: voidspace/gomaasapi
func subnet_2_0(source map[string]interface{}) (*subnet, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),
		"id":           schema.ForceInt(),
		"name":         schema.String(),
		"space":        schema.String(),
		"gateway_ip":   schema.OneOf(schema.Nil(""), schema.String()),
		"cidr":         schema.String(),
		"vlan":         schema.StringMap(schema.Any()),
		"dns_servers":  schema.OneOf(schema.Nil(""), schema.List(schema.String())),
	}
	checker := schema.FieldMap(fields, nil) // no defaults
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "subnet 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	vlan, err := vlan_2_0(valid["vlan"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Since the gateway_ip is optional, we use the two part cast assignment. If
	// the cast fails, then we get the default value we care about, which is the
	// empty string.
	gateway, _ := valid["gateway_ip"].(string)

	result := &subnet{
		resourceURI: valid["resource_uri"].(string),
		id:          valid["id"].(int),
		name:        valid["name"].(string),
		space:       valid["space"].(string),
		vlan:        vlan,
		gateway:     gateway,
		cidr:        valid["cidr"].(string),
		dnsServers:  convertToStringSlice(valid["dns_servers"]),
	}
	return result, nil
}
コード例 #8
0
ファイル: partition.go プロジェクト: voidspace/gomaasapi
func partition_2_0(source map[string]interface{}) (*partition, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),

		"id":   schema.ForceInt(),
		"path": schema.String(),
		"uuid": schema.String(),

		"used_for": schema.String(),
		"size":     schema.ForceUint(),

		"filesystem": schema.OneOf(schema.Nil(""), schema.StringMap(schema.Any())),
	}
	checker := schema.FieldMap(fields, nil)
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, WrapWithDeserializationError(err, "partition 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	var filesystem *filesystem
	if fsSource := valid["filesystem"]; fsSource != nil {
		filesystem, err = filesystem2_0(fsSource.(map[string]interface{}))
		if err != nil {
			return nil, errors.Trace(err)
		}
	}

	result := &partition{
		resourceURI: valid["resource_uri"].(string),
		id:          valid["id"].(int),
		path:        valid["path"].(string),
		uuid:        valid["uuid"].(string),
		usedFor:     valid["used_for"].(string),
		size:        valid["size"].(uint64),
		filesystem:  filesystem,
	}
	return result, nil
}
コード例 #9
0
ファイル: config_test.go プロジェクト: xushiwei/juju
func (s *configSuite) TestSecretAttrsAreStrings(c *gc.C) {
	for i, field := range configSecretFields {
		c.Logf("test %d: %s", i, field)
		attrs := validAttrs().Merge(testing.Attrs{field: 0})

		if v, ok := configFields[field]; ok {
			configFields[field] = schema.ForceInt()
			defer func(c schema.Checker) {
				configFields[field] = c
			}(v)
		} else {
			c.Errorf("secrect field %s not found in configFields", field)
			continue
		}

		testConfig := newConfig(c, attrs)
		sa, err := providerInstance.SecretAttrs(testConfig)
		c.Check(sa, gc.IsNil)
		c.Check(err, gc.ErrorMatches, "secret .* field must have a string value; got .*")
	}
}
コード例 #10
0
ファイル: fabric.go プロジェクト: voidspace/gomaasapi
func fabric_2_0(source map[string]interface{}) (*fabric, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),
		"id":           schema.ForceInt(),
		"name":         schema.String(),
		"class_type":   schema.OneOf(schema.Nil(""), schema.String()),
		"vlans":        schema.List(schema.StringMap(schema.Any())),
	}
	checker := schema.FieldMap(fields, nil) // no defaults
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, errors.Annotatef(err, "fabric 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	vlans, err := readVLANList(valid["vlans"].([]interface{}), vlan_2_0)
	if err != nil {
		return nil, errors.Trace(err)
	}

	// Since the class_type is optional, we use the two part cast assignment. If
	// the cast fails, then we get the default value we care about, which is the
	// empty string.
	classType, _ := valid["class_type"].(string)

	result := &fabric{
		resourceURI: valid["resource_uri"].(string),
		id:          valid["id"].(int),
		name:        valid["name"].(string),
		classType:   classType,
		vlans:       vlans,
	}
	return result, nil
}
コード例 #11
0
ファイル: config.go プロジェクト: bac/juju
	if uuid, ok := c[ControllerUUIDKey].(string); ok && !utils.IsValidUUIDString(uuid) {
		return errors.Errorf("controller-uuid: expected UUID, got string(%q)", uuid)
	}

	return nil
}

// GenerateControllerCertAndKey makes sure that the config has a CACert and
// CAPrivateKey, generates and returns new certificate and key.
func GenerateControllerCertAndKey(caCert, caKey string, hostAddresses []string) (string, string, error) {
	return cert.NewDefaultServer(caCert, caKey, hostAddresses)
}

var configChecker = schema.FieldMap(schema.Fields{
	AuditingEnabled:         schema.Bool(),
	APIPort:                 schema.ForceInt(),
	StatePort:               schema.ForceInt(),
	IdentityURL:             schema.String(),
	IdentityPublicKey:       schema.String(),
	SetNUMAControlPolicyKey: schema.Bool(),
	AutocertURLKey:          schema.String(),
	AutocertDNSNameKey:      schema.String(),
	AllowModelAccessKey:     schema.Bool(),
}, schema.Defaults{
	APIPort:                 DefaultAPIPort,
	AuditingEnabled:         DefaultAuditingEnabled,
	StatePort:               DefaultStatePort,
	IdentityURL:             schema.Omit,
	IdentityPublicKey:       schema.Omit,
	SetNUMAControlPolicyKey: DefaultNUMAControlPolicy,
	AutocertURLKey:          schema.Omit,
コード例 #12
0
ファイル: ebs.go プロジェクト: pmatulis/juju
// ebsProvider creates volume sources which use AWS EBS volumes.
type ebsProvider struct{}

var _ storage.Provider = (*ebsProvider)(nil)

var ebsConfigFields = schema.Fields{
	EBS_VolumeType: schema.OneOf(
		schema.Const(volumeTypeMagnetic),
		schema.Const(volumeTypeSsd),
		schema.Const(volumeTypeProvisionedIops),
		schema.Const(volumeTypeStandard),
		schema.Const(volumeTypeGp2),
		schema.Const(volumeTypeIo1),
	),
	EBS_IOPS:      schema.ForceInt(),
	EBS_Encrypted: schema.Bool(),
}

var ebsConfigChecker = schema.FieldMap(
	ebsConfigFields,
	schema.Defaults{
		EBS_VolumeType: volumeTypeMagnetic,
		EBS_IOPS:       schema.Omit,
		EBS_Encrypted:  false,
	},
)

type ebsConfig struct {
	volumeType string
	iops       int
コード例 #13
0
ファイル: fields.go プロジェクト: cmars/oo
// FieldType describes the type of an attribute value.
type FieldType string

// The following constants are the possible type values.
const (
	Tstring FieldType = "string"
	Tbool   FieldType = "bool"
	Tint    FieldType = "int"
	Tattrs  FieldType = "attrs"
)

var checkers = map[FieldType]schema.Checker{
	Tstring: schema.String(),
	Tbool:   schema.Bool(),
	Tint:    schema.ForceInt(),
	Tattrs:  attrsC{},
}

// Alternative possibilities to ValidationSchema to bear in mind for
// the future:
// func (s Fields) Checker() schema.Checker
// func (s Fields) Validate(value map[string]interface{}) (v map[string] interface{}, extra []string, err error)

// ValidationSchema returns values suitable for passing to
// schema.FieldMap to create a schema.Checker that will validate the given fields.
// It will return an error if the fields are invalid.
//
// The Defaults return value will contain entries for all non-mandatory
// attributes set to schema.Omit. It is the responsibility of the
// client to set any actual default values as required.
コード例 #14
0
ファイル: machine.go プロジェクト: voidspace/gomaasapi
func machine_2_0(source map[string]interface{}) (*machine, error) {
	fields := schema.Fields{
		"resource_uri": schema.String(),

		"system_id": schema.String(),
		"hostname":  schema.String(),
		"fqdn":      schema.String(),
		"tag_names": schema.List(schema.String()),

		"osystem":       schema.String(),
		"distro_series": schema.String(),
		"architecture":  schema.String(),
		"memory":        schema.ForceInt(),
		"cpu_count":     schema.ForceInt(),

		"ip_addresses":   schema.List(schema.String()),
		"power_state":    schema.String(),
		"status_name":    schema.String(),
		"status_message": schema.String(),

		"boot_interface": schema.StringMap(schema.Any()),
		"interface_set":  schema.List(schema.StringMap(schema.Any())),
		"zone":           schema.StringMap(schema.Any()),

		"physicalblockdevice_set": schema.List(schema.StringMap(schema.Any())),
		"blockdevice_set":         schema.List(schema.StringMap(schema.Any())),
	}
	checker := schema.FieldMap(fields, nil) // no defaults
	coerced, err := checker.Coerce(source, nil)
	if err != nil {
		return nil, WrapWithDeserializationError(err, "machine 2.0 schema check failed")
	}
	valid := coerced.(map[string]interface{})
	// From here we know that the map returned from the schema coercion
	// contains fields of the right type.

	bootInterface, err := interface_2_0(valid["boot_interface"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Trace(err)
	}
	interfaceSet, err := readInterfaceList(valid["interface_set"].([]interface{}), interface_2_0)
	if err != nil {
		return nil, errors.Trace(err)
	}
	zone, err := zone_2_0(valid["zone"].(map[string]interface{}))
	if err != nil {
		return nil, errors.Trace(err)
	}
	physicalBlockDevices, err := readBlockDeviceList(valid["physicalblockdevice_set"].([]interface{}), blockdevice_2_0)
	if err != nil {
		return nil, errors.Trace(err)
	}
	blockDevices, err := readBlockDeviceList(valid["blockdevice_set"].([]interface{}), blockdevice_2_0)
	if err != nil {
		return nil, errors.Trace(err)
	}

	result := &machine{
		resourceURI: valid["resource_uri"].(string),

		systemID: valid["system_id"].(string),
		hostname: valid["hostname"].(string),
		fqdn:     valid["fqdn"].(string),
		tags:     convertToStringSlice(valid["tag_names"]),

		operatingSystem: valid["osystem"].(string),
		distroSeries:    valid["distro_series"].(string),
		architecture:    valid["architecture"].(string),
		memory:          valid["memory"].(int),
		cpuCount:        valid["cpu_count"].(int),

		ipAddresses:   convertToStringSlice(valid["ip_addresses"]),
		powerState:    valid["power_state"].(string),
		statusName:    valid["status_name"].(string),
		statusMessage: valid["status_message"].(string),

		bootInterface:        bootInterface,
		interfaceSet:         interfaceSet,
		zone:                 zone,
		physicalBlockDevices: physicalBlockDevices,
		blockDevices:         blockDevices,
	}

	return result, nil
}
コード例 #15
0
ファイル: config.go プロジェクト: jiasir/juju
import (
	"fmt"

	"github.com/juju/schema"

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

const defaultStoragePort = 8040

var (
	configFields = schema.Fields{
		"bootstrap-host":    schema.String(),
		"bootstrap-user":    schema.String(),
		"storage-listen-ip": schema.String(),
		"storage-port":      schema.ForceInt(),
		"storage-auth-key":  schema.String(),
		"use-sshstorage":    schema.Bool(),
	}
	configDefaults = schema.Defaults{
		"bootstrap-user":    "",
		"storage-listen-ip": "",
		"storage-port":      defaultStoragePort,
		"use-sshstorage":    true,
	}
)

type environConfig struct {
	*config.Config
	attrs map[string]interface{}
}
コード例 #16
0
ファイル: config.go プロジェクト: rogpeppe/juju
	"default-series":            schema.String(),
	"tools-metadata-url":        schema.String(),
	"image-metadata-url":        schema.String(),
	"image-stream":              schema.String(),
	"authorized-keys":           schema.String(),
	"authorized-keys-path":      schema.String(),
	"firewall-mode":             schema.String(),
	"agent-version":             schema.String(),
	"development":               schema.Bool(),
	"admin-secret":              schema.String(),
	"ca-cert":                   schema.String(),
	"ca-cert-path":              schema.String(),
	"ca-private-key":            schema.String(),
	"ca-private-key-path":       schema.String(),
	"ssl-hostname-verification": schema.Bool(),
	"state-port":                schema.ForceInt(),
	"api-port":                  schema.ForceInt(),
	"syslog-port":               schema.ForceInt(),
	"rsyslog-ca-cert":           schema.String(),
	"logging-config":            schema.String(),
	"charm-store-auth":          schema.String(),
	"provisioner-safe-mode":     schema.Bool(),
	"http-proxy":                schema.String(),
	"https-proxy":               schema.String(),
	"ftp-proxy":                 schema.String(),
	"no-proxy":                  schema.String(),
	"apt-http-proxy":            schema.String(),
	"apt-https-proxy":           schema.String(),
	"apt-ftp-proxy":             schema.String(),
	"bootstrap-timeout":         schema.ForceInt(),
	"bootstrap-retry-delay":     schema.ForceInt(),
コード例 #17
0
ファイル: config.go プロジェクト: bac/juju
	}
	data, err := ioutil.ReadFile(absPath)
	if err != nil {
		return "", userSpecified, errors.Annotatef(err, "%q not set, and could not read from %q", key, path)
	}
	if len(data) == 0 {
		return "", userSpecified, errors.Errorf("file %q is empty", path)
	}
	return string(data), userSpecified, nil
}

var configChecker = schema.FieldMap(schema.Fields{
	AdminSecretKey:             schema.String(),
	CACertKey:                  schema.String(),
	CACertKey + "-path":        schema.String(),
	CAPrivateKeyKey:            schema.String(),
	CAPrivateKeyKey + "-path":  schema.String(),
	BootstrapTimeoutKey:        schema.ForceInt(),
	BootstrapRetryDelayKey:     schema.ForceInt(),
	BootstrapAddressesDelayKey: schema.ForceInt(),
}, schema.Defaults{
	AdminSecretKey:             schema.Omit,
	CACertKey:                  schema.Omit,
	CACertKey + "-path":        schema.Omit,
	CAPrivateKeyKey:            schema.Omit,
	CAPrivateKeyKey + "-path":  schema.Omit,
	BootstrapTimeoutKey:        DefaultBootstrapSSHTimeout,
	BootstrapRetryDelayKey:     DefaultBootstrapSSHRetryDelay,
	BootstrapAddressesDelayKey: DefaultBootstrapSSHAddressesDelay,
})