func (r *DiskSubresource) DiffGeneral() error {
	log.Printf("[DEBUG] %s: Beginning diff validation", r)
	name, err := diskLabelOrName(r.data)
	if err != nil {
		return err
	}

	// Enforce the maximum unit number, which is the current value of
	// scsi_controller_count * 15 - 1.
	ctlrCount := r.rdd.Get("scsi_controller_count").(int)
	maxUnit := ctlrCount*15 - 1
	currentUnit := r.Get("unit_number").(int)
	if currentUnit > maxUnit {
		return fmt.Errorf("unit_number on disk %q too high (%d) - maximum value is %d with %d SCSI controller(s)", name, currentUnit, maxUnit, ctlrCount)
	}

	if r.Get("attach").(bool) {
		switch {
		case r.Get("datastore_id").(string) == "":
			return fmt.Errorf("datastore_id for disk %q is required when attach is set", name)
		case r.Get("size").(int) > 0:
			return fmt.Errorf("size for disk %q cannot be defined when attach is set", name)
		case r.Get("eagerly_scrub").(bool):
			return fmt.Errorf("eagerly_scrub for disk %q cannot be defined when attach is set", name)
		case r.Get("keep_on_remove").(bool):
			return fmt.Errorf("keep_on_remove for disk %q is implicit when attach is set, please remove this setting", name)
		}
	} else {
		// Enforce size as a required field when attach is not set
		if r.Get("size").(int) < 1 {
			return fmt.Errorf("size for disk %q: required option not set", name)
		}
	}
	// Block certain options from being set depending on the vSphere version.
	version := viapi.ParseVersionFromClient(r.client)
	if r.Get("disk_sharing").(string) != string(types.VirtualDiskSharingSharingNone) {
		if version.Older(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
			return fmt.Errorf("multi-writer disk_sharing is only supported on vSphere 6 and higher")
		}
	}
	// Prevent eagerly_scrub and thin_provisioned from both being set to true. A
	// thin_provisioned disk cannot be eagerly scrubbed since it would then be
	// allocating the entire disk.
	if r.Get("eagerly_scrub").(bool) && r.Get("thin_provisioned").(bool) {
		return fmt.Errorf("%s: eagerly_scrub and thin_provisioned cannot both be set to true", name)
	}
	log.Printf("[DEBUG] %s: Diff validation complete", r)
	return nil
}

// DiffGeneral performs complex validation of an individual disk sub-resource
// that can't be done in schema alone. Should be run on new and existing disks.

DiffGeneral performs complex validation of an individual disk sub - resource that can t be done in schema alone . Should be run on new and existing disks .


func (r *DiskSubresource) normalizeDiskDatastore() error {
	podID := r.rdd.Get("datastore_cluster_id").(string)
	dsID, _ := r.GetChange("datastore_id")

	if podID == "" {
		// We don't have a storage pod, just set the old ID and exit. We don't need
		// to worry about whether or not the storage pod is computed here as if it
		// is, the VM datastore will have been marked as computed and this function
		// will have never ran.
		r.Set("datastore_id", dsID)
		return nil
	}

	log.Printf("[DEBUG] %s: Checking datastore cluster membership of disk", r)

	pod, err := storagepod.FromID(r.client, podID)
	if err != nil {
		return fmt.Errorf("error fetching datastore cluster ID %q: %s", podID, err)
	}

	ds, err := datastore.FromID(r.client, dsID.(string))
	if err != nil {
		return fmt.Errorf("error fetching datastore ID %q: %s", dsID, err)
	}

	isMember, err := storagepod.IsMember(pod, ds)
	if err != nil {
		return fmt.Errorf("error checking storage pod membership: %s", err)
	}
	if !isMember {
		log.Printf(
			"[DEBUG] %s: Disk's datastore %q not a member of cluster %q, marking datastore ID as computed",
			r,
			ds.Name(),
			pod.Name(),
		)
		dsID = diskDatastoreComputedName
	}

	r.Set("datastore_id", dsID)
	return nil
}

// normalizeDiskDatastore normalizes the datastore_id field in a disk
// sub-resource. If the VM has a datastore cluster defined, it checks to make
// sure the datastore in the current state of the disk is a member of the
// currently defined datastore cluster, and if it is not, it marks the disk as
// computed so that it can be migrated back to the datastore cluster on the
// next update.

normalizeDiskDatastore normalizes the datastore_id field in a disk sub - resource . If the VM has a datastore cluster defined it checks to make sure the datastore in the current state of the disk is a member of the currently defined datastore cluster and if it is not it marks the disk as computed so that it can be migrated back to the datastore cluster on the next update .


func (r *DiskSubresource) Relocate(l object.VirtualDeviceList, clone bool) (types.VirtualMachineRelocateSpecDiskLocator, error) {
	log.Printf("[DEBUG] %s: Starting relocate generation", r)
	disk, err := r.findVirtualDisk(l, clone)
	var relocate types.VirtualMachineRelocateSpecDiskLocator
	if err != nil {
		return relocate, fmt.Errorf("cannot find disk device: %s", err)
	}

	// Expand all of the necessary disk settings first. This ensures all backing
	// data is properly populate and updated.
	if err := r.expandDiskSettings(disk); err != nil {
		return relocate, err
	}

	relocate.DiskId = disk.Key

	// Set the datastore for the relocation
	dsID := r.Get("datastore_id").(string)
	if dsID == "" {
		// Default to the default datastore
		dsID = r.rdd.Get("datastore_id").(string)
	}
	ds, err := datastore.FromID(r.client, dsID)
	if err != nil {
		return relocate, err
	}
	dsref := ds.Reference()
	relocate.Datastore = dsref

	// Add additional backing options if we are cloning.
	if r.rdd.Id() == "" {
		log.Printf("[DEBUG] %s: Adding additional options to relocator for cloning", r)

		backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
		backing.FileName = ds.Path("")
		backing.Datastore = &dsref
		relocate.DiskBackingInfo = backing
	}

	// Done!
	log.Printf("[DEBUG] %s: Generated disk locator: %s", r, diskRelocateString(relocate))
	log.Printf("[DEBUG] %s: Relocate generation complete", r)
	return relocate, nil
}

// Relocate produces a VirtualMachineRelocateSpecDiskLocator for this resource
// and is used for both cloning and storage vMotion.

Relocate produces a VirtualMachineRelocateSpecDiskLocator for this resource and is used for both cloning and storage vMotion .


func (r *DiskSubresource) String() string {
	p := diskPathOrName(r.data)
	if p == "" {
		p = "<unknown>"
	}
	return fmt.Sprintf("%s (%s)", r.Subresource.String(), p)
}

func ( r * DiskSubresource ) String ( ) string { p := diskPathOrName ( r . data ) if p == " " { p = " " } return fmt . Sprintf ( " " , r . Subresource . String ( ) , p ) }

// String prints out the disk sub-resource's information including the ID at
// time of instantiation, the path of the disk, and the current device
// key and address.

String prints out the disk sub - resource s information including the ID at time of instantiation the path of the disk and the current device key and address .


func (r *DiskSubresource) expandDiskSettings(disk *types.VirtualDisk) error {
	// Backing settings
	b := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
	b.DiskMode = r.GetWithRestart("disk_mode").(string)
	b.WriteThrough = structure.BoolPtr(r.GetWithRestart("write_through").(bool))

	// Only use disk_sharing if we are on vSphere 6.0 and higher
	version := viapi.ParseVersionFromClient(r.client)
	if version.Newer(viapi.VSphereVersion{Product: version.Product, Major: 6}) {
		b.Sharing = r.GetWithRestart("disk_sharing").(string)
	}

	// This settings are only set for internal disks
	if !r.Get("attach").(bool) {
		var err error
		var v interface{}
		if v, err = r.GetWithVeto("thin_provisioned"); err != nil {
			return err
		}
		b.ThinProvisioned = structure.BoolPtr(v.(bool))

		if v, err = r.GetWithVeto("eagerly_scrub"); err != nil {
			return err
		}
		b.EagerlyScrub = structure.BoolPtr(v.(bool))

		// Disk settings
		os, ns := r.GetChange("size")
		if os.(int) > ns.(int) {
			return fmt.Errorf("virtual disks cannot be shrunk")
		}
		disk.CapacityInBytes = structure.GiBToByte(ns.(int))
		disk.CapacityInKB = disk.CapacityInBytes / 1024
	}

	alloc := &types.StorageIOAllocationInfo{
		Limit:       structure.Int64Ptr(int64(r.Get("io_limit").(int))),
		Reservation: structure.Int32Ptr(int32(r.Get("io_reservation").(int))),
		Shares: &types.SharesInfo{
			Shares: int32(r.Get("io_share_count").(int)),
			Level:  types.SharesLevel(r.Get("io_share_level").(string)),
		},
	}
	disk.StorageIOAllocation = alloc

	return nil
}

// expandDiskSettings sets appropriate fields on an existing disk - this is
// used during Create and Update to set attributes to those found in
// configuration.

expandDiskSettings sets appropriate fields on an existing disk - this is used during Create and Update to set attributes to those found in configuration .


func (r *DiskSubresource) createDisk(l object.VirtualDeviceList) (*types.VirtualDisk, error) {
	disk := new(types.VirtualDisk)
	disk.Backing = new(types.VirtualDiskFlatVer2BackingInfo)

	// Only assign backing info if a datastore cluster is not specified. If one
	// is, skip this step.
	if r.rdd.Get("datastore_cluster_id").(string) == "" {
		if err := r.assignBackingInfo(disk); err != nil {
			return nil, err
		}
	}

	// Set a new device key for this device
	disk.Key = l.NewKey()
	return disk, nil
}

// createDisk performs all of the logic for a base virtual disk creation.

createDisk performs all of the logic for a base virtual disk creation .


func (r *DiskSubresource) assignDisk(l object.VirtualDeviceList, disk *types.VirtualDisk) (types.BaseVirtualController, error) {
	number := r.Get("unit_number").(int)
	// Figure out the bus number, and look up the SCSI controller that matches
	// that. You can attach 15 disks to a SCSI controller, and we allow a maximum
	// of 30 devices.
	bus := number / 15
	// Also determine the unit number on that controller.
	unit := int32(math.Mod(float64(number), 15))

	// Find the controller.
	ctlr, err := r.ControllerForCreateUpdate(l, SubresourceControllerTypeSCSI, bus)
	if err != nil {
		return nil, err
	}

	// Build the unit list.
	units := make([]bool, 16)
	// Reserve the SCSI unit number
	scsiUnit := ctlr.(types.BaseVirtualSCSIController).GetVirtualSCSIController().ScsiCtlrUnitNumber
	units[scsiUnit] = true

	ckey := ctlr.GetVirtualController().Key

	for _, device := range l {
		d := device.GetVirtualDevice()
		if d.ControllerKey != ckey || d.UnitNumber == nil {
			continue
		}
		units[*d.UnitNumber] = true
	}

	// We now have a valid list of units. If we need to, shift up the desired
	// unit number so it's not taking the unit of the controller itself.
	if unit >= scsiUnit {
		unit++
	}

	if units[unit] {
		return nil, fmt.Errorf("unit number %d on SCSI bus %d is in use", unit, bus)
	}

	// If we made it this far, we are good to go!
	disk.ControllerKey = ctlr.GetVirtualController().Key
	disk.UnitNumber = &unit
	return ctlr, nil
}

// assignDisk takes a unit number and assigns it correctly to a controller on
// the SCSI bus. An error is returned if the assigned unit number is taken.

assignDisk takes a unit number and assigns it correctly to a controller on the SCSI bus . An error is returned if the assigned unit number is taken .


func (r *Subresource) findControllerInfo(l object.VirtualDeviceList, disk *types.VirtualDisk) (int, types.BaseVirtualController, error) {
	ctlr := l.FindByKey(disk.ControllerKey)
	if ctlr == nil {
		return -1, nil, fmt.Errorf("could not find disk controller with key %d for disk key %d", disk.ControllerKey, disk.Key)
	}
	if disk.UnitNumber == nil {
		return -1, nil, fmt.Errorf("unit number on disk key %d is unset", disk.Key)
	}
	sc, ok := ctlr.(types.BaseVirtualSCSIController)
	if !ok {
		return -1, nil, fmt.Errorf("controller at key %d is not a SCSI controller (actual: %T)", ctlr.GetVirtualDevice().Key, ctlr)
	}
	unit := *disk.UnitNumber
	if unit > sc.GetVirtualSCSIController().ScsiCtlrUnitNumber {
		unit--
	}
	unit = unit + 15*sc.GetVirtualSCSIController().BusNumber
	return int(unit), ctlr.(types.BaseVirtualController), nil
}

func ( r * Subresource ) findControllerInfo ( l object . VirtualDeviceList , disk * types . VirtualDisk ) ( int , types . BaseVirtualController , error ) { ctlr := l . FindByKey ( disk . ControllerKey ) if ctlr == nil { return - 1 , nil , fmt . Errorf ( " " , disk . ControllerKey , disk . Key ) } if disk . UnitNumber == nil { return - 1 , nil , fmt . Errorf ( " " , disk . Key ) } sc , ok := ctlr . ( types . BaseVirtualSCSIController ) if ! ok { return - 1 , nil , fmt . Errorf ( " " , ctlr . GetVirtualDevice ( ) . Key , ctlr ) } unit := * disk . UnitNumber if unit > sc . GetVirtualSCSIController ( ) . ScsiCtlrUnitNumber { unit -- } unit = unit + 15 * sc . GetVirtualSCSIController ( ) . BusNumber return int ( unit ) , ctlr . ( types . BaseVirtualController ) , nil }

// findControllerInfo determines the normalized unit number for the disk device
// based on the SCSI controller and unit number it's connected to. The
// controller is also returned.

findControllerInfo determines the normalized unit number for the disk device based on the SCSI controller and unit number it s connected to . The controller is also returned .


func diskRelocateListString(relocators []types.VirtualMachineRelocateSpecDiskLocator) string {
	var out []string
	for _, relocate := range relocators {
		out = append(out, diskRelocateString(relocate))
	}
	return strings.Join(out, ",")
}

func diskRelocateListString ( relocators [ ] types . VirtualMachineRelocateSpecDiskLocator ) string { var out [ ] string for _ , relocate := range relocators { out = append ( out , diskRelocateString ( relocate ) ) } return strings . Join ( out , " " ) }

// diskRelocateListString pretty-prints a list of
// VirtualMachineRelocateSpecDiskLocator.

diskRelocateListString pretty - prints a list of VirtualMachineRelocateSpecDiskLocator .


func diskRelocateString(relocate types.VirtualMachineRelocateSpecDiskLocator) string {
	key := relocate.DiskId
	var locstring string
	if backing, ok := relocate.DiskBackingInfo.(*types.VirtualDiskFlatVer2BackingInfo); ok && backing != nil {
		locstring = backing.FileName
	} else {
		locstring = relocate.Datastore.Value
	}
	return fmt.Sprintf("(%d => %s)", key, locstring)
}

func diskRelocateString ( relocate types . VirtualMachineRelocateSpecDiskLocator ) string { key := relocate . DiskId var locstring string if backing , ok := relocate . DiskBackingInfo . ( * types . VirtualDiskFlatVer2BackingInfo ) ; ok && backing != nil { locstring = backing . FileName } else { locstring = relocate . Datastore . Value } return fmt . Sprintf ( " " , key , locstring ) }

// diskRelocateString prints out information from a
// VirtualMachineRelocateSpecDiskLocator in a friendly way.
//
// The format depends on whether or not a backing has been defined.

diskRelocateString prints out information from a VirtualMachineRelocateSpecDiskLocator in a friendly way . The format depends on whether or not a backing has been defined .


func (l virtualDeviceListSorter) Less(i, j int) bool {
	li := l.Sort[i]
	lj := l.Sort[j]
	liCtlr := l.DeviceList.FindByKey(li.GetVirtualDevice().ControllerKey)
	ljCtlr := l.DeviceList.FindByKey(lj.GetVirtualDevice().ControllerKey)
	if liCtlr == nil || ljCtlr == nil {
		panic(errors.New("virtualDeviceListSorter cannot be used with devices that are not assigned to a controller"))
	}
	if liCtlr.(types.BaseVirtualController).GetVirtualController().BusNumber < liCtlr.(types.BaseVirtualController).GetVirtualController().BusNumber {
		return true
	}
	liUnit := li.GetVirtualDevice().UnitNumber
	ljUnit := lj.GetVirtualDevice().UnitNumber
	if liUnit == nil || ljUnit == nil {
		panic(errors.New("virtualDeviceListSorter cannot be used with devices that do not have unit numbers set"))
	}
	return *liUnit < *ljUnit
}

func ( l virtualDeviceListSorter ) Less ( i , j int ) bool { li := l . Sort [ i ] lj := l . Sort [ j ] liCtlr := l . DeviceList . FindByKey ( li . GetVirtualDevice ( ) . ControllerKey ) ljCtlr := l . DeviceList . FindByKey ( lj . GetVirtualDevice ( ) . ControllerKey ) if liCtlr == nil || ljCtlr == nil { panic ( errors . New ( " " ) ) } if liCtlr . ( types . BaseVirtualController ) . GetVirtualController ( ) . BusNumber < liCtlr . ( types . BaseVirtualController ) . GetVirtualController ( ) . BusNumber { return true } liUnit := li . GetVirtualDevice ( ) . UnitNumber ljUnit := lj . GetVirtualDevice ( ) . UnitNumber if liUnit == nil || ljUnit == nil { panic ( errors . New ( " " ) ) } return * liUnit < * ljUnit }

// Less helps implement sort.Interface for virtualDeviceListSorter. A
// BaseVirtualDevice is "less" than another device if its controller's bus
// number and unit number combination are earlier in the order than the other.

Less helps implement sort . Interface for virtualDeviceListSorter . A BaseVirtualDevice is less than another device if its controller s bus number and unit number combination are earlier in the order than the other .


func (l virtualDeviceListSorter) Swap(i, j int) {
	l.Sort[i], l.Sort[j] = l.Sort[j], l.Sort[i]
}

func ( l virtualDeviceListSorter ) Swap ( i , j int ) { l . Sort [ i ] , l . Sort [ j ] = l . Sort [ j ] , l . Sort [ i ] }

// Swap helps implement sort.Interface for virtualDeviceListSorter.

Swap helps implement sort . Interface for virtualDeviceListSorter .


func (s virtualDiskSubresourceSorter) Less(i, j int) bool {
	mi := s[i].(map[string]interface{})
	mj := s[j].(map[string]interface{})
	return mi["unit_number"].(int) < mj["unit_number"].(int)
}

func ( s virtualDiskSubresourceSorter ) Less ( i , j int ) bool { mi := s [ i ] . ( map [ string ] interface { } ) mj := s [ j ] . ( map [ string ] interface { } ) return mi [ " " ] . ( int ) < mj [ " " ] . ( int ) }

// Less helps implement sort.Interface for virtualDiskSubresourceSorter.

Less helps implement sort . Interface for virtualDiskSubresourceSorter .


func (s virtualDiskSubresourceSorter) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func ( s virtualDiskSubresourceSorter ) Swap ( i , j int ) { s [ i ] , s [ j ] = s [ j ] , s [ i ] }

// Swap helps implement sort.Interface for virtualDiskSubresourceSorter.

Swap helps implement sort . Interface for virtualDiskSubresourceSorter .


func datastorePathHasBase(p, b string) bool {
	dp := &object.DatastorePath{}
	if ok := dp.FromString(p); !ok {
		return false
	}
	return path.Base(dp.Path) == path.Base(b)
}

func datastorePathHasBase ( p , b string ) bool { dp := & object . DatastorePath { } if ok := dp . FromString ( p ) ; ! ok { return false } return path . Base ( dp . Path ) == path . Base ( b ) }

// datastorePathHasBase is a helper to check if a datastore path's file matches
// a supplied file name.

datastorePathHasBase is a helper to check if a datastore path s file matches a supplied file name .


func (r *DiskSubresource) findVirtualDisk(l object.VirtualDeviceList, fallback bool) (*types.VirtualDisk, error) {
	device, err := r.findVirtualDiskByUUIDOrAddress(l, fallback)
	if err != nil {
		return nil, err
	}
	return device.(*types.VirtualDisk), nil
}

func ( r * DiskSubresource ) findVirtualDisk ( l object . VirtualDeviceList , fallback bool ) ( * types . VirtualDisk , error ) { device , err := r . findVirtualDiskByUUIDOrAddress ( l , fallback ) if err != nil { return nil , err } return device . ( * types . VirtualDisk ) , nil }

// findVirtualDisk locates a virtual disk by it UUID, or by its device address
// if UUID is missing.
//
// The device address search is only used if fallback is true - this is so that
// we can distinguish situations where it should be used, such as a read,
// versus situations where it should never be used, such as an update or
// delete.

findVirtualDisk locates a virtual disk by it UUID or by its device address if UUID is missing . The device address search is only used if fallback is true - this is so that we can distinguish situations where it should be used such as a read versus situations where it should never be used such as an update or delete .


func diskCapacityInGiB(disk *types.VirtualDisk) int {
	if disk.CapacityInBytes > 0 {
		return int(structure.ByteToGiB(disk.CapacityInBytes).(int64))
	}
	log.Printf(
		"[DEBUG] diskCapacityInGiB: capacityInBytes missing for for %s, falling back to capacityInKB",
		object.VirtualDeviceList{}.Name(disk),
	)
	return int(structure.ByteToGiB(disk.CapacityInKB * 1024).(int64))
}

func diskCapacityInGiB ( disk * types . VirtualDisk ) int { if disk . CapacityInBytes > 0 { return int ( structure . ByteToGiB ( disk . CapacityInBytes ) . ( int64 ) ) } log . Printf ( " " , object . VirtualDeviceList { } . Name ( disk ) , ) return int ( structure . ByteToGiB ( disk . CapacityInKB * 1024 ) . ( int64 ) ) }

// diskCapacityInGiB reports the supplied disk's capacity, by first checking
// CapacityInBytes, and then falling back to CapacityInKB if that value is
// unavailable. This helps correct some situations where the former value's
// data gets cleared, which seems to happen on upgrades.

diskCapacityInGiB reports the supplied disk s capacity by first checking CapacityInBytes and then falling back to CapacityInKB if that value is unavailable . This helps correct some situations where the former value s data gets cleared which seems to happen on upgrades .


func hostNetworkSystemFromHostSystem(hs *object.HostSystem) (*object.HostNetworkSystem, error) {
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()
	return hs.ConfigManager().NetworkSystem(ctx)
}

func hostNetworkSystemFromHostSystem ( hs * object . HostSystem ) ( * object . HostNetworkSystem , error ) { ctx , cancel := context . WithTimeout ( context . Background ( ) , defaultAPITimeout ) defer cancel ( ) return hs . ConfigManager ( ) . NetworkSystem ( ctx ) }

// hostNetworkSystemFromHostSystem locates a HostNetworkSystem from a specified
// HostSystem.

hostNetworkSystemFromHostSystem locates a HostNetworkSystem from a specified HostSystem .


func hostNetworkSystemFromHostSystemID(client *govmomi.Client, hsID string) (*object.HostNetworkSystem, error) {
	hs, err := hostsystem.FromID(client, hsID)
	if err != nil {
		return nil, err
	}
	return hostNetworkSystemFromHostSystem(hs)
}

func hostNetworkSystemFromHostSystemID ( client * govmomi . Client , hsID string ) ( * object . HostNetworkSystem , error ) { hs , err := hostsystem . FromID ( client , hsID ) if err != nil { return nil , err } return hostNetworkSystemFromHostSystem ( hs ) }

// hostNetworkSystemFromHostSystemID locates a HostNetworkSystem from a
// specified HostSystem managed object ID.

hostNetworkSystemFromHostSystemID locates a HostNetworkSystem from a specified HostSystem managed object ID .


func hostVSwitchFromName(client *govmomi.Client, ns *object.HostNetworkSystem, name string) (*types.HostVirtualSwitch, error) {
	var mns mo.HostNetworkSystem
	pc := client.PropertyCollector()
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()
	if err := pc.RetrieveOne(ctx, ns.Reference(), []string{"networkInfo.vswitch"}, &mns); err != nil {
		return nil, fmt.Errorf("error fetching host network properties: %s", err)
	}

	for _, sw := range mns.NetworkInfo.Vswitch {
		if sw.Name == name {
			// Spec.Mtu is not set for vSwitches created directly
			// in ESXi. TODO: Use sw.Mtu instead of sw.Spec.Mtu in
			// flattenHostVirtualSwitchSpec.
			sw.Spec.Mtu = sw.Mtu
			return &sw, nil
		}
	}

	return nil, fmt.Errorf("could not find virtual switch %s", name)
}

// hostVSwitchFromName locates a virtual switch on the supplied
// HostNetworkSystem by name.

hostVSwitchFromName locates a virtual switch on the supplied HostNetworkSystem by name .


func hostPortGroupFromName(client *govmomi.Client, ns *object.HostNetworkSystem, name string) (*types.HostPortGroup, error) {
	var mns mo.HostNetworkSystem
	pc := client.PropertyCollector()
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()
	if err := pc.RetrieveOne(ctx, ns.Reference(), []string{"networkInfo.portgroup"}, &mns); err != nil {
		return nil, fmt.Errorf("error fetching host network properties: %s", err)
	}

	for _, pg := range mns.NetworkInfo.Portgroup {
		if pg.Spec.Name == name {
			return &pg, nil
		}
	}

	return nil, fmt.Errorf("could not find port group %s", name)
}

func hostPortGroupFromName ( client * govmomi . Client , ns * object . HostNetworkSystem , name string ) ( * types . HostPortGroup , error ) { var mns mo . HostNetworkSystem pc := client . PropertyCollector ( ) ctx , cancel := context . WithTimeout ( context . Background ( ) , defaultAPITimeout ) defer cancel ( ) if err := pc . RetrieveOne ( ctx , ns . Reference ( ) , [ ] string { " " } , & mns ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } for _ , pg := range mns . NetworkInfo . Portgroup { if pg . Spec . Name == name { return & pg , nil } } return nil , fmt . Errorf ( " " , name ) }

// hostPortGroupFromName locates a port group on the supplied HostNetworkSystem
// by name.

hostPortGroupFromName locates a port group on the supplied HostNetworkSystem by name .


func networkObjectFromHostSystem(client *govmomi.Client, hs *object.HostSystem, name string) (*object.Network, error) {
	// Validate vCenter as this function is only relevant there
	if err := viapi.ValidateVirtualCenter(client); err != nil {
		return nil, err
	}
	finder := find.NewFinder(client.Client, false)
	ctx, cancel := context.WithTimeout(context.Background(), defaultAPITimeout)
	defer cancel()
	nets, err := finder.NetworkList(ctx, "*/"+name)
	if err != nil {
		return nil, err
	}

	for _, n := range nets {
		net, ok := n.(*object.Network)
		if !ok {
			// Not a standard port group (possibly DVS, etc), pass
			continue
		}
		props, err := network.Properties(net)
		if err != nil {
			return nil, err
		}
		for _, hsRef := range props.Host {
			if hsRef.Value == hs.Reference().Value {
				// This is our network
				return net, nil
			}
		}
	}

	return nil, fmt.Errorf("could not find a matching %q on host ID %q", name, hs.Reference().Value)
}

// networkObjectFromHostSystem locates the network object in vCenter for a
// specific HostSystem and network name.
//
// It does this by searching for all networks in the folder hierarchy that
// match the given network name for the HostSystem's managed object reference
// ID. This match is returned - if nothing is found, an error is given.

networkObjectFromHostSystem locates the network object in vCenter for a specific HostSystem and network name . It does this by searching for all networks in the folder hierarchy that match the given network name for the HostSystem s managed object reference ID . This match is returned - if nothing is found an error is given .


func resourceVSphereLicenseUpdate(d *schema.ResourceData, meta interface{}) error {
	log.Println("[INFO] Running the update method")

	client := meta.(*VSphereClient).vimClient
	manager := license.NewManager(client.Client)

	if key, ok := d.GetOk("license_key"); ok {
		licenseKey := key.(string)
		if !isKeyPresent(licenseKey, manager) {
			return ErrNoSuchKeyFound
		}

		if d.HasChange("labels") {
			labelMap := d.Get("labels").(map[string]interface{})

			err := updateLabels(manager, licenseKey, labelMap)
			if err != nil {
				return err
			}
		}
	}

	return resourceVSphereLicenseRead(d, meta)
}

func resourceVSphereLicenseUpdate ( d * schema . ResourceData , meta interface { } ) error { log . Println ( " " ) client := meta . ( * VSphereClient ) . vimClient manager := license . NewManager ( client . Client ) if key , ok := d . GetOk ( " " ) ; ok { licenseKey := key . ( string ) if ! isKeyPresent ( licenseKey , manager ) { return ErrNoSuchKeyFound } if d . HasChange ( " " ) { labelMap := d . Get ( " " ) . ( map [ string ] interface { } ) err := updateLabels ( manager , licenseKey , labelMap ) if err != nil { return err } } } return resourceVSphereLicenseRead ( d , meta ) }

// resourceVSphereLicenseUpdate check for change in labels of the key and updates them.

resourceVSphereLicenseUpdate check for change in labels of the key and updates them .


func isKeyPresent(key string, manager *license.Manager) bool {
	infoList, _ := manager.List(context.TODO())

	for _, info := range infoList {
		if info.LicenseKey == key {
			return true
		}
	}

	return false
}

func isKeyPresent ( key string , manager * license . Manager ) bool { infoList , _ := manager . List ( context . TODO ( ) ) for _ , info := range infoList { if info . LicenseKey == key { return true } } return false }

// isKeyPresent iterates over the InfoList to check if the license is present or not.

isKeyPresent iterates over the InfoList to check if the license is present or not .


func UpdateLabel(ctx context.Context, m *license.Manager, licenseKey string, key string, val string) error {
	req := types.UpdateLicenseLabel{
		This:       m.Reference(),
		LicenseKey: licenseKey,
		LabelKey:   key,
		LabelValue: val,
	}

	_, err := methods.UpdateLicenseLabel(ctx, m.Client(), &req)
	return err
}

func UpdateLabel ( ctx context . Context , m * license . Manager , licenseKey string , key string , val string ) error { req := types . UpdateLicenseLabel { This : m . Reference ( ) , LicenseKey : licenseKey , LabelKey : key , LabelValue : val , } _ , err := methods . UpdateLicenseLabel ( ctx , m . Client ( ) , & req ) return err }

// UpdateLabel provides a wrapper around the UpdateLabel data objects


func DecodeError(info types.LicenseManagerLicenseInfo) error {
	for _, property := range info.Properties {
		if property.Key == "diagnostic" {
			return errors.New(property.Value.(string))
		}
	}

	return nil
}

func DecodeError ( info types . LicenseManagerLicenseInfo ) error { for _ , property := range info . Properties { if property . Key == " " { return errors . New ( property . Value . ( string ) ) } } return nil }

// DecodeError tries to find a specific error which occurs when an invalid key is passed
// to the server

DecodeError tries to find a specific error which occurs when an invalid key is passed to the server


func resourceVSphereVAppContainerApplyTags(d *schema.ResourceData, meta interface{}, va *object.VirtualApp) error {
	tagsClient, err := tagsClientIfDefined(d, meta)
	if err != nil {
		return err
	}

	// Apply any pending tags now.
	if tagsClient == nil {
		log.Printf("[DEBUG] %s: Tags unsupported on this connection, skipping", resourceVSphereComputeClusterIDString(d))
		return nil
	}

	log.Printf("[DEBUG] %s: Applying any pending tags", resourceVSphereVAppContainerIDString(d))
	return processTagDiff(tagsClient, d, va)
}

func
resourceVSphereVAppContainerApplyTags
(
d
*
schema
.
ResourceData
,
meta
interface
{
}
,
va
*
object
.
VirtualApp
)
error
{
tagsClient
,
err
:=
tagsClientIfDefined
(
d
,
meta
)
if
err
!=
{
return
err
}
// Apply any pending tags now.
if
tagsClient
==
{
log
.
Printf
(
"
"
,
resourceVSphereComputeClusterIDString
(
d
)
)
return
}
log
.
Printf
(
"
"
,
resourceVSphereVAppContainerIDString
(
d
)
)
return
processTagDiff
(
tagsClient
,
d
,
va
)
}

// resourceVSphereVAppContainerApplyTags processes the tags step for both create
// and update for vsphere_vapp_container.

resourceVSphereVAppContainerApplyTags processes the tags step for both create and update for vsphere_vapp_container .


func resourceVSphereVAppContainerReadTags(d *schema.ResourceData, meta interface{}, va *object.VirtualApp) error {
	if tagsClient, _ := meta.(*VSphereClient).TagsClient(); tagsClient != nil {
		log.Printf("[DEBUG] %s: Reading tags", resourceVSphereVAppContainerIDString(d))
		if err := readTagsForResource(tagsClient, va, d); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] %s: Tags unsupported on this connection, skipping tag read", resourceVSphereVAppContainerIDString(d))
	}
	return nil
}

func resourceVSphereVAppContainerReadTags ( d * schema . ResourceData , meta interface { } , va * object . VirtualApp ) error { if tagsClient , _ := meta . ( * VSphereClient ) . TagsClient ( ) ; tagsClient != nil { log . Printf ( " " , resourceVSphereVAppContainerIDString ( d ) ) if err := readTagsForResource ( tagsClient , va , d ) ; err != nil { return err } } else { log . Printf ( " " , resourceVSphereVAppContainerIDString ( d ) ) } return nil }

// resourceVSphereVAppContainerReadTags reads the tags for
// vsphere_vapp_container.

resourceVSphereVAppContainerReadTags reads the tags for vsphere_vapp_container .


func expandClusterVMHostRuleInfo(d *schema.ResourceData) (*types.ClusterVmHostRuleInfo, error) {
	obj := &types.ClusterVmHostRuleInfo{
		ClusterRuleInfo: types.ClusterRuleInfo{
			Enabled:     structure.GetBool(d, "enabled"),
			Mandatory:   structure.GetBool(d, "mandatory"),
			Name:        d.Get("name").(string),
			UserCreated: structure.BoolPtr(true),
		},
		AffineHostGroupName:     d.Get("affinity_host_group_name").(string),
		AntiAffineHostGroupName: d.Get("anti_affinity_host_group_name").(string),
		VmGroupName:             d.Get("vm_group_name").(string),
	}
	return obj, nil
}

func expandClusterVMHostRuleInfo ( d * schema . ResourceData ) ( * types . ClusterVmHostRuleInfo , error ) { obj := & types . ClusterVmHostRuleInfo { ClusterRuleInfo : types . ClusterRuleInfo { Enabled : structure . GetBool ( d , " " ) , Mandatory : structure . GetBool ( d , " " ) , Name : d . Get ( " " ) . ( string ) , UserCreated : structure . BoolPtr ( true ) , } , AffineHostGroupName : d . Get ( " " ) . ( string ) , AntiAffineHostGroupName : d . Get ( " " ) . ( string ) , VmGroupName : d . Get ( " " ) . ( string ) , } return obj , nil }

// expandClusterVMHostRuleInfo reads certain ResourceData keys and returns a
// ClusterVmHostRuleInfo.

expandClusterVMHostRuleInfo reads certain ResourceData keys and returns a ClusterVmHostRuleInfo .


func flattenClusterVMHostRuleInfo(d *schema.ResourceData, obj *types.ClusterVmHostRuleInfo) error {
	return structure.SetBatch(d, map[string]interface{}{
		"enabled":                       obj.Enabled,
		"mandatory":                     obj.Mandatory,
		"name":                          obj.Name,
		"affinity_host_group_name":      obj.AffineHostGroupName,
		"anti_affinity_host_group_name": obj.AntiAffineHostGroupName,
		"vm_group_name":                 obj.VmGroupName,
	})
}

func flattenClusterVMHostRuleInfo ( d * schema . ResourceData , obj * types . ClusterVmHostRuleInfo ) error { return structure . SetBatch ( d , map [ string ] interface { } { " " : obj . Enabled , " " : obj . Mandatory , " " : obj . Name , " " : obj . AffineHostGroupName , " " : obj . AntiAffineHostGroupName , " " : obj . VmGroupName , } ) }

// flattenClusterVMHostRuleInfo saves a ClusterVmHostRuleInfo into the supplied ResourceData.

flattenClusterVMHostRuleInfo saves a ClusterVmHostRuleInfo into the supplied ResourceData .


func resourceVSphereVirtualMachineMigrateState(version int, os *terraform.InstanceState, meta interface{}) (*terraform.InstanceState, error) {
	// Guard against a nil state.
	if os == nil {
		return nil, nil
	}

	// Guard against empty state, can't do anything with it
	if os.Empty() {
		return os, nil
	}

	var migrateFunc func(*terraform.InstanceState, interface{}) error
	switch version {
	case 2:
		log.Printf("[DEBUG] Migrating vsphere_virtual_machine state: old v%d state: %#v", version, os)
		migrateFunc = migrateVSphereVirtualMachineStateV3
	case 1:
		log.Printf("[DEBUG] Migrating vsphere_virtual_machine state: old v%d state: %#v", version, os)
		migrateFunc = migrateVSphereVirtualMachineStateV2
	case 0:
		log.Printf("[DEBUG] Migrating vsphere_virtual_machine state: old v%d state: %#v", version, os)
		migrateFunc = migrateVSphereVirtualMachineStateV1
	default:
		// Migration is complete
		log.Printf("[DEBUG] Migrating vsphere_virtual_machine state: completed v%d state: %#v", version, os)
		return os, nil
	}
	if err := migrateFunc(os, meta); err != nil {
		return nil, err
	}
	version++
	log.Printf("[DEBUG] Migrating vsphere_virtual_machine state: new v%d state: %#v", version, os)
	return resourceVSphereVirtualMachineMigrateState(version, os, meta)
}

// resourceVSphereVirtualMachineMigrateState is the master state migration function for
// the vsphere_virtual_machine resource.


func migrateVSphereVirtualMachineStateV3(is *terraform.InstanceState, meta interface{}) error {
	// All we really preserve from the old state is the UUID of the virtual
	// machine. We leverage some of the special parts of the import functionality
	// - namely validating disks, and flagging the VM as imported in the state to
	// guard against someone adding customization to the configuration and
	// accidentally forcing a new resource.
	//
	// Read will handle most of the population post-migration as it does for
	// import, and there will be an unavoidable diff for TF-only options on the
	// next plan.
	client := meta.(*VSphereClient).vimClient
	id := is.ID

	log.Printf("[DEBUG] Migrate state for VM at UUID %q", id)
	vm, err := virtualmachine.FromUUID(client, id)
	if err != nil {
		return fmt.Errorf("error fetching virtual machine: %s", err)
	}
	props, err := virtualmachine.Properties(vm)
	if err != nil {
		return fmt.Errorf("error fetching virtual machine properties: %s", err)
	}

	// Populate the UUID field of all virtual disks in state.
	diskCnt, _ := strconv.Atoi(is.Attributes["disk.#"])
	l := object.VirtualDeviceList(props.Config.Hardware.Device)
	for i := 0; i < diskCnt; i++ {
		v, ok := is.Attributes[fmt.Sprintf("disk.%d.key", i)]
		if !ok {
			return fmt.Errorf("corrupt state: key disk.%d.key not found", i)
		}
		key, err := strconv.Atoi(v)
		if err != nil {
			return fmt.Errorf("corrupt state: strconv.Atoi error on disk.%d.key: %s", i, err)
		}
		if key < 1 {
			// This is a possibility during v1 -> v3 migrations, and would fail to
			// find a device anyway, so we just ignore these.
			continue
		}
		device := l.FindByKey(int32(key))
		if device == nil {
			// Missing device, pass
			continue
		}
		disk, ok := device.(*types.VirtualDisk)
		if !ok {
			// Not the device we are looking for
			continue
		}
		backing, ok := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
		if !ok {
			// Someone has tampered with the VM to the point where we really should
			// not mess with it. We can't account for all cases, but if someone has
			// added something like an RDM disk or something else that is not
			// VMDK-backed, we don't want to continue. We have never supported this.
			return fmt.Errorf("disk device %s is not a VMDK-backed virtual disk and state import cannot continue", l.Name(disk))
		}
		is.Attributes[fmt.Sprintf("disk.%d.uuid", i)] = backing.Uuid
	}

	d := resourceVSphereVirtualMachine().Data(&terraform.InstanceState{})
	log.Printf("[DEBUG] %s: Migration to V3 complete", resourceVSphereVirtualMachineIDString(d))
	return nil
}

// migrateVSphereVirtualMachineStateV3 migrates the state of the
// vsphere_virtual_machine from version 2 to version 3.

migrateVSphereVirtualMachineStateV3 migrates the state of the vsphere_virtual_machine from version 2 to version 3 .


func NewEnvironmentBrowser(c *vim25.Client, ref types.ManagedObjectReference) *EnvironmentBrowser {
	return &EnvironmentBrowser{
		Common: object.NewCommon(c, ref),
	}
}

func NewEnvironmentBrowser ( c * vim25 . Client , ref types . ManagedObjectReference ) * EnvironmentBrowser { return & EnvironmentBrowser { Common : object . NewCommon ( c , ref ) , } }

// NewEnvironmentBrowser initializes a new EnvironmentBrowser based off the
// supplied managed object reference.

NewEnvironmentBrowser initializes a new EnvironmentBrowser based off the supplied managed object reference .


func (b *EnvironmentBrowser) DefaultDevices(ctx context.Context, key string, host *object.HostSystem) (object.VirtualDeviceList, error) {
	var eb mo.EnvironmentBrowser

	err := b.Properties(ctx, b.Reference(), nil, &eb)
	if err != nil {
		return nil, err
	}

	req := types.QueryConfigOption{
		This: b.Reference(),
		Key:  key,
	}
	if host != nil {
		ref := host.Reference()
		req.Host = &ref
	}
	res, err := methods.QueryConfigOption(ctx, b.Client(), &req)
	if err != nil {
		return nil, err
	}
	if res.Returnval == nil {
		return nil, errors.New("no config options were found for the supplied criteria")
	}
	return object.VirtualDeviceList(res.Returnval.DefaultDevice), nil
}

func ( b * EnvironmentBrowser ) DefaultDevices ( ctx context . Context , key string , host * object . HostSystem ) ( object . VirtualDeviceList , error ) { var eb mo . EnvironmentBrowser err := b . Properties ( ctx , b . Reference ( ) , nil , & eb ) if err != nil { return nil , err } req := types . QueryConfigOption { This : b . Reference ( ) , Key : key , } if host != nil { ref := host . Reference ( ) req . Host = & ref } res , err := methods . QueryConfigOption ( ctx , b . Client ( ) , & req ) if err != nil { return nil , err } if res . Returnval == nil { return nil , errors . New ( " " ) } return object . VirtualDeviceList ( res . Returnval . DefaultDevice ) , nil }

// DefaultDevices loads a satisfactory default device list for the optionally
// supplied host and descriptor key. The result is returned as a higher-level
// VirtualDeviceList object. This can be used as an initial VirtualDeviceList
// when building a device list and VirtualDeviceConfigSpec list for new virtual
// machines.
//
// Appropriate options for key can be loaded by running
// QueryConfigOptionDescriptor, which will return a list of
// VirtualMachineConfigOptionDescriptor which will contain the appropriate key
// for the virtual machine version needed. If no key is supplied, the results
// generally reflect the most recent VM hardware version.

DefaultDevices loads a satisfactory default device list for the optionally supplied host and descriptor key . The result is returned as a higher - level VirtualDeviceList object . This can be used as an initial VirtualDeviceList when building a device list and VirtualDeviceConfigSpec list for new virtual machines . Appropriate options for key can be loaded by running QueryConfigOptionDescriptor which will return a list of VirtualMachineConfigOptionDescriptor which will contain the appropriate key for the virtual machine version needed . If no key is supplied the results generally reflect the most recent VM hardware version .


func (b *EnvironmentBrowser) OSFamily(ctx context.Context, guest string) (string, error) {
	var eb mo.EnvironmentBrowser

	err := b.Properties(ctx, b.Reference(), nil, &eb)
	if err != nil {
		return "", err
	}

	req := types.QueryConfigOption{
		This: b.Reference(),
	}
	res, err := methods.QueryConfigOption(ctx, b.Client(), &req)
	if err != nil {
		return "", err
	}
	if res.Returnval == nil {
		return "", errors.New("no config options were found for the supplied criteria")
	}
	for _, osd := range res.Returnval.GuestOSDescriptor {
		if osd.Id == guest {
			family := osd.Family
			log.Printf("[DEBUG] OSFamily: family for %q is %q", guest, family)
			return family, nil
		}
	}
	return "", fmt.Errorf("could not find guest ID %q", guest)
}

func ( b * EnvironmentBrowser ) OSFamily ( ctx context . Context , guest string ) ( string , error ) { var eb mo . EnvironmentBrowser err := b . Properties ( ctx , b . Reference ( ) , nil , & eb ) if err != nil { return " " , err } req := types . QueryConfigOption { This : b . Reference ( ) , } res , err := methods . QueryConfigOption ( ctx , b . Client ( ) , & req ) if err != nil { return " " , err } if res . Returnval == nil { return " " , errors . New ( " " ) } for _ , osd := range res . Returnval . GuestOSDescriptor { if osd . Id == guest { family := osd . Family log . Printf ( " " , guest , family ) return family , nil } } return " " , fmt . Errorf ( " " , guest ) }

// OSFamily fetches the operating system family for the supplied guest ID.

OSFamily fetches the operating system family for the supplied guest ID .


func (b *EnvironmentBrowser) QueryConfigOptionDescriptor(ctx context.Context) ([]types.VirtualMachineConfigOptionDescriptor, error) {
	req := types.QueryConfigOptionDescriptor{
		This: b.Reference(),
	}
	res, err := methods.QueryConfigOptionDescriptor(ctx, b.Client(), &req)
	if err != nil {
		return nil, err
	}
	return res.Returnval, nil
}

func ( b * EnvironmentBrowser ) QueryConfigOptionDescriptor ( ctx context . Context ) ( [ ] types . VirtualMachineConfigOptionDescriptor , error ) { req := types . QueryConfigOptionDescriptor { This : b . Reference ( ) , } res , err := methods . QueryConfigOptionDescriptor ( ctx , b . Client ( ) , & req ) if err != nil { return nil , err } return res . Returnval , nil }

// QueryConfigOptionDescriptor returns a list the list of ConfigOption keys
// available on the environment that this browser targets. The keys can be used
// as query options for DefaultDevices and other functions, facilitating the
// specification of results specific to a certain VM version.

QueryConfigOptionDescriptor returns a list the list of ConfigOption keys available on the environment that this browser targets . The keys can be used as query options for DefaultDevices and other functions facilitating the specification of results specific to a certain VM version .


func (p RootPathParticle) RootFromDatacenter(dc *object.Datacenter) string {
	return dc.InventoryPath + "/" + string(p)
}

func ( p RootPathParticle ) RootFromDatacenter ( dc * object . Datacenter ) string { return dc . InventoryPath + " " + string ( p ) }

// RootFromDatacenter returns the root path for the particle from the given
// datacenter's inventory path.

RootFromDatacenter returns the root path for the particle from the given datacenter s inventory path .


func (p RootPathParticle) PathFromDatacenter(dc *object.Datacenter, relative string) string {
	return p.RootFromDatacenter(dc) + "/" + relative
}

func ( p RootPathParticle ) PathFromDatacenter ( dc * object . Datacenter , relative string ) string { return p . RootFromDatacenter ( dc ) + " " + relative }

// PathFromDatacenter returns the combined result of RootFromDatacenter plus a
// relative path for a given particle and datacenter object.

PathFromDatacenter returns the combined result of RootFromDatacenter plus a relative path for a given particle and datacenter object .


func (p RootPathParticle) SplitDatacenter(inventoryPath string) (string, error) {
	s := strings.SplitN(inventoryPath, p.Delimiter(), 2)
	if len(s) != 2 {
		return inventoryPath, fmt.Errorf("could not split path %q on %q", inventoryPath, p.Delimiter())
	}
	return s[0], nil
}

func ( p RootPathParticle ) SplitDatacenter ( inventoryPath string ) ( string , error ) { s := strings . SplitN ( inventoryPath , p . Delimiter ( ) , 2 ) if len ( s ) != 2 { return inventoryPath , fmt . Errorf ( " " , inventoryPath , p . Delimiter ( ) ) } return s [ 0 ] , nil }

// SplitDatacenter is a convenience method that splits out the datacenter path
// from the supplied path for the particle.

SplitDatacenter is a convenience method that splits out the datacenter path from the supplied path for the particle .


func (p RootPathParticle) SplitRelativeFolder(inventoryPath string) (string, error) {
	relative, err := p.SplitRelative(inventoryPath)
	if err != nil {
		return inventoryPath, err
	}
	return path.Dir(relative), nil
}

func ( p RootPathParticle ) SplitRelativeFolder ( inventoryPath string ) ( string , error ) { relative , err := p . SplitRelative ( inventoryPath ) if err != nil { return inventoryPath , err } return path . Dir ( relative ) , nil }

// SplitRelativeFolder is a convenience method that returns the parent folder
// for the result of SplitRelative on the supplied path.
//
// This is generally useful to get the folder for a managed entity, versus getting a full relative path. If you want that, use SplitRelative instead.

SplitRelativeFolder is a convenience method that returns the parent folder for the result of SplitRelative on the supplied path . This is generally useful to get the folder for a managed entity versus getting a full relative path . If you want that use SplitRelative instead .


func (p RootPathParticle) NewRootFromPath(inventoryPath string, newParticle RootPathParticle) (string, error) {
	dcPath, err := p.SplitDatacenter(inventoryPath)
	if err != nil {
		return inventoryPath, err
	}
	return fmt.Sprintf("%s/%s", dcPath, newParticle), nil
}

func ( p RootPathParticle ) NewRootFromPath ( inventoryPath string , newParticle RootPathParticle ) ( string , error ) { dcPath , err := p . SplitDatacenter ( inventoryPath ) if err != nil { return inventoryPath , err } return fmt . Sprintf ( " " , dcPath , newParticle ) , nil }

// NewRootFromPath takes the datacenter path for a specific entity, and then
// appends the new particle supplied.

NewRootFromPath takes the datacenter path for a specific entity and then appends the new particle supplied .


func datacenterPathFromHostSystemID(client *govmomi.Client, hsID string) (string, error) {
	hs, err := hostsystem.FromID(client, hsID)
	if err != nil {
		return "", err
	}
	return RootPathParticleHost.SplitDatacenter(hs.InventoryPath)
}

func datacenterPathFromHostSystemID ( client * govmomi . Client , hsID string ) ( string , error ) { hs , err := hostsystem . FromID ( client , hsID ) if err != nil { return " " , err } return RootPathParticleHost . SplitDatacenter ( hs . InventoryPath ) }

// datacenterPathFromHostSystemID returns the datacenter section of a
// HostSystem's inventory path.

datacenterPathFromHostSystemID returns the datacenter section of a HostSystem s inventory path .


func datastoreRootPathFromHostSystemID(client *govmomi.Client, hsID string) (string, error) {
	hs, err := hostsystem.FromID(client, hsID)
	if err != nil {
		return "", err
	}
	return RootPathParticleHost.NewRootFromPath(hs.InventoryPath, RootPathParticleDatastore)
}

func datastoreRootPathFromHostSystemID ( client * govmomi . Client , hsID string ) ( string , error ) { hs , err := hostsystem . FromID ( client , hsID ) if err != nil { return " " , err } return RootPathParticleHost . NewRootFromPath ( hs . InventoryPath , RootPathParticleDatastore ) }

// datastoreRootPathFromHostSystemID returns the root datastore folder path
// for a specific host system ID.

datastoreRootPathFromHostSystemID returns the root datastore folder path for a specific host system ID .


func validateDatastoreFolder(folder *object.Folder) (*object.Folder, error) {
	ft, err := FindType(folder)
	if err != nil {
		return nil, err
	}
	if ft != VSphereFolderTypeDatastore {
		return nil, fmt.Errorf("%q is not a datastore folder", folder.InventoryPath)
	}
	return folder, nil
}

func validateDatastoreFolder ( folder * object . Folder ) ( * object . Folder , error ) { ft , err := FindType ( folder ) if err != nil { return nil , err } if ft != VSphereFolderTypeDatastore { return nil , fmt . Errorf ( " " , folder . InventoryPath ) } return folder , nil }

// validateDatastoreFolder checks to make sure the folder is a datastore
// folder, and returns it if it is, or an error if it isn't.

validateDatastoreFolder checks to make sure the folder is a datastore folder and returns it if it is or an error if it isn t .


func validateHostFolder(folder *object.Folder) (*object.Folder, error) {
	ft, err := FindType(folder)
	if err != nil {
		return nil, err
	}
	if ft != VSphereFolderTypeHost {
		return nil, fmt.Errorf("%q is not a host folder", folder.InventoryPath)
	}
	return folder, nil
}

func validateHostFolder ( folder * object . Folder ) ( * object . Folder , error ) { ft , err := FindType ( folder ) if err != nil { return nil , err } if ft != VSphereFolderTypeHost { return nil , fmt . Errorf ( " " , folder . InventoryPath ) } return folder , nil }

// validateHostFolder checks to make sure the folder is a host
// folder, and returns it if it is, or an error if it isn't.

validateHostFolder checks to make sure the folder is a host folder and returns it if it is or an error if it isn t .


func validateVirtualMachineFolder(folder *object.Folder) (*object.Folder, error) {
	ft, err := FindType(folder)
	if err != nil {
		return nil, err
	}
	if ft != VSphereFolderTypeVM {
		return nil, fmt.Errorf("%q is not a VM folder", folder.InventoryPath)
	}
	log.Printf("[DEBUG] Folder located: %q", folder.InventoryPath)
	return folder, nil
}

func validateVirtualMachineFolder ( folder * object . Folder ) ( * object . Folder , error ) { ft , err := FindType ( folder ) if err != nil { return nil , err } if ft != VSphereFolderTypeVM { return nil , fmt . Errorf ( " " , folder . InventoryPath ) } log . Printf ( " " , folder . InventoryPath ) return folder , nil }

// validateVirtualMachineFolder checks to make sure the folder is a VM folder,
// and returns it if it is, or an error if it isn't.

validateVirtualMachineFolder checks to make sure the folder is a VM folder and returns it if it is or an error if it isn t .


func validateNetworkFolder(folder *object.Folder) (*object.Folder, error) {
	ft, err := FindType(folder)
	if err != nil {
		return nil, err
	}
	if ft != VSphereFolderTypeNetwork {
		return nil, fmt.Errorf("%q is not a network folder", folder.InventoryPath)
	}
	return folder, nil
}

func validateNetworkFolder ( folder * object . Folder ) ( * object . Folder , error ) { ft , err := FindType ( folder ) if err != nil { return nil , err } if ft != VSphereFolderTypeNetwork { return nil , fmt . Errorf ( " " , folder . InventoryPath ) } return folder , nil }

// validateNetworkFolder checks to make sure the folder is a network folder,
// and returns it if it is, or an error if it isn't.

validateNetworkFolder checks to make sure the folder is a network folder and returns it if it is or an error if it isn t .


func NormalizePath(v interface{}) string {
	p := v.(string)
	if PathIsEmpty(p) {
		return ""
	}
	return strings.TrimPrefix(path.Clean(p), "/")
}

func NormalizePath ( v interface { } ) string { p := v . ( string ) if PathIsEmpty ( p ) { return " " } return strings . TrimPrefix ( path . Clean ( p ) , " " ) }

// NormalizePath is a SchemaStateFunc that normalizes a folder path.

NormalizePath is a SchemaStateFunc that normalizes a folder path .


func MoveObjectTo(ref types.ManagedObjectReference, folder *object.Folder) error {
	ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer cancel()
	task, err := folder.MoveInto(ctx, []types.ManagedObjectReference{ref})
	if err != nil {
		return err
	}
	tctx, tcancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer tcancel()
	return task.Wait(tctx)
}

func MoveObjectTo ( ref types . ManagedObjectReference , folder * object . Folder ) error { ctx , cancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer cancel ( ) task , err := folder . MoveInto ( ctx , [ ] types . ManagedObjectReference { ref } ) if err != nil { return err } tctx , tcancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer tcancel ( ) return task . Wait ( tctx ) }

// MoveObjectTo moves a object by reference into a folder.

MoveObjectTo moves a object by reference into a folder .


func Properties(folder *object.Folder) (*mo.Folder, error) {
	ctx, cancel := context.WithTimeout(context.Background(), provider.DefaultAPITimeout)
	defer cancel()
	var props mo.Folder
	if err := folder.Properties(ctx, folder.Reference(), nil, &props); err != nil {
		return nil, err
	}
	return &props, nil
}

func Properties ( folder * object . Folder ) ( * mo . Folder , error ) { ctx , cancel := context . WithTimeout ( context . Background ( ) , provider . DefaultAPITimeout ) defer cancel ( ) var props mo . Folder if err := folder . Properties ( ctx , folder . Reference ( ) , nil , & props ) ; err != nil { return nil , err } return & props , nil }

// Properties is a convenience method that wraps fetching the
// Folder MO from its higher-level object.

Properties is a convenience method that wraps fetching the Folder MO from its higher - level object .


func FindType(folder *object.Folder) (VSphereFolderType, error) {
	var ft VSphereFolderType

	props, err := Properties(folder)
	if err != nil {
		return ft, err
	}

	// Depending on the container type, the actual folder type may be contained
	// in either the first or second element, the former for clusters, datastore
	// clusters, or standalone ESXi for VMs, and the latter in the case of actual
	// folders that can contain subfolders.
	var ct string
	if props.ChildType[0] != "Folder" {
		ct = props.ChildType[0]
	} else {
		ct = props.ChildType[1]
	}

	switch ct {
	case "Datacenter":
		ft = VSphereFolderTypeDatacenter
	case "ComputeResource":
		ft = VSphereFolderTypeHost
	case "VirtualMachine":
		ft = VSphereFolderTypeVM
	case "Datastore":
		ft = VSphereFolderTypeDatastore
	case "Network":
		ft = VSphereFolderTypeNetwork
	default:
		return ft, fmt.Errorf("unknown folder type: %#v", ct)
	}

	return ft, nil
}

// FindType returns a proper VSphereFolderType for a folder object by checking its child type.

FindType returns a proper VSphereFolderType for a folder object by checking its child type .


func (opts ListOpts) ToServerListQuery() (string, error) {
	q, err := gophercloud.BuildQueryString(opts)
	return q.String(), err
}

func ( opts ListOpts ) ToServerListQuery ( ) ( string , error ) { q , err := gophercloud . BuildQueryString ( opts ) return q . String ( ) , err }

// ToServerListQuery formats a ListOpts into a query string.

ToServerListQuery formats a ListOpts into a query string .


func Index(vs []string, t string) int {
	for i, v := range vs {
		if v == t {
			return i
		}
	}
	return -1
}

func Index ( vs [ ] string , t string ) int { for i , v := range vs { if v == t { return i } } return - 1 }

// Index returns the first index of the target string t, or -1 if no match is found.

Index returns the first index of the target string t or - 1 if no match is found .


func Any(vs []string, f func(string) bool) bool {
	for _, v := range vs {
		if f(v) {
			return true
		}
	}
	return false
}

func Any ( vs [ ] string , f func ( string ) bool ) bool { for _ , v := range vs { if f ( v ) { return true } } return false }

//Any returns true if one of the strings in the slice satisfies the predicate f.

Any returns true if one of the strings in the slice satisfies the predicate f .


func client(path string) (*http.Client, error) {
	if path == "" {
		return google.DefaultClient(oauth2.NoContext, compute.ComputeScope)
	}

	key, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}

	jwtConfig, err := google.JWTConfigFromJSON(key, compute.ComputeScope)
	if err != nil {
		return nil, err
	}

	return jwtConfig.Client(oauth2.NoContext), nil
}

func client ( path string ) ( * http . Client , error ) { if path == " " { return google . DefaultClient ( oauth2 . NoContext , compute . ComputeScope ) } key , err := ioutil . ReadFile ( path ) if err != nil { return nil , err } jwtConfig , err := google . JWTConfigFromJSON ( key , compute . ComputeScope ) if err != nil { return nil , err } return jwtConfig . Client ( oauth2 . NoContext ) , nil }

// client returns an authenticated HTTP client for use with GCE.

client returns an authenticated HTTP client for use with GCE .


func lookupProject() (string, error) {
	req, err := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/project/project-id", nil)
	if err != nil {
		return "", err
	}
	req.Header.Add("Metadata-Flavor", "Google")

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	if resp.StatusCode != 200 {
		return "", fmt.Errorf("discover-gce: invalid status code %d when fetching project id", resp.StatusCode)
	}

	project, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(project), nil
}

func lookupProject ( ) ( string , error ) { req , err := http . NewRequest ( " " , " " , nil ) if err != nil { return " " , err } req . Header . Add ( " " , " " ) resp , err := http . DefaultClient . Do ( req ) if err != nil { return " " , err } defer resp . Body . Close ( ) if resp . StatusCode != 200 { return " " , fmt . Errorf ( " " , resp . StatusCode ) } project , err := ioutil . ReadAll ( resp . Body ) if err != nil { return " " , err } return string ( project ) , nil }

// lookupProject retrieves the project name from the metadata of the current node.

lookupProject retrieves the project name from the metadata of the current node .


func lookupZones(svc *compute.Service, project, pattern string) ([]string, error) {
	call := svc.Zones.List(project)
	if pattern != "" {
		call = call.Filter("name eq " + pattern)
	}

	var zones []string
	f := func(page *compute.ZoneList) error {
		for _, v := range page.Items {
			zones = append(zones, v.Name)
		}
		return nil
	}

	if err := call.Pages(oauth2.NoContext, f); err != nil {
		return nil, err
	}
	return zones, nil
}

func lookupZones ( svc * compute . Service , project , pattern string ) ( [ ] string , error ) { call := svc . Zones . List ( project ) if pattern != " " { call = call . Filter ( " " + pattern ) } var zones [ ] string f := func ( page * compute . ZoneList ) error { for _ , v := range page . Items { zones = append ( zones , v . Name ) } return nil } if err := call . Pages ( oauth2 . NoContext , f ) ; err != nil { return nil , err } return zones , nil }

// lookupZones retrieves the zones of the project and filters them by pattern.

lookupZones retrieves the zones of the project and filters them by pattern .


func lookupAddrs(svc *compute.Service, project, zone, tag string) ([]string, error) {
	var addrs []string
	f := func(page *compute.InstanceList) error {
		for _, v := range page.Items {
			if len(v.NetworkInterfaces) == 0 || v.NetworkInterfaces[0].NetworkIP == "" {
				continue
			}
			for _, t := range v.Tags.Items {
				if t == tag {
					addrs = append(addrs, v.NetworkInterfaces[0].NetworkIP)
					break
				}
			}
		}
		return nil
	}

	call := svc.Instances.List(project, zone)
	if err := call.Pages(oauth2.NoContext, f); err != nil {
		return nil, err
	}
	return addrs, nil
}

func lookupAddrs ( svc * compute . Service , project , zone , tag string ) ( [ ] string , error ) { var addrs [ ] string f := func ( page * compute . InstanceList ) error { for _ , v := range page . Items { if len ( v . NetworkInterfaces ) == 0 || v . NetworkInterfaces [ 0 ] . NetworkIP == " " { continue } for _ , t := range v . Tags . Items { if t == tag { addrs = append ( addrs , v . NetworkInterfaces [ 0 ] . NetworkIP ) break } } } return nil } call := svc . Instances . List ( project , zone ) if err := call . Pages ( oauth2 . NoContext , f ) ; err != nil { return nil , err } return addrs , nil }

// lookupAddrs retrieves the private ip addresses of all instances in a given
// project and zone which have a matching tag value.

lookupAddrs retrieves the private ip addresses of all instances in a given project and zone which have a matching tag value .


func New(opts ...Option) (*Discover, error) {
	d := new(Discover)

	for _, opt := range opts {
		if err := opt(d); err != nil {
			return nil, err
		}
	}

	d.once.Do(d.initProviders)

	return d, nil
}

func New ( opts ... Option ) ( * Discover , error ) { d := new ( Discover ) for _ , opt := range opts { if err := opt ( d ) ; err != nil { return nil , err } } d . once . Do ( d . initProviders ) return d , nil }

// New creates a new discover client with the given options.

New creates a new discover client with the given options .


func WithUserAgent(agent string) Option {
	return func(d *Discover) error {
		d.userAgent = agent
		return nil
	}
}

func WithUserAgent ( agent string ) Option { return func ( d * Discover ) error { d . userAgent = agent return nil } }

// WithUserAgent allows specifying a custom user agent option to send with
// requests when the underlying client library supports it.

WithUserAgent allows specifying a custom user agent option to send with requests when the underlying client library supports it .


func WithProviders(m map[string]Provider) Option {
	return func(d *Discover) error {
		d.Providers = m
		return nil
	}
}

func WithProviders ( m map [ string ] Provider ) Option { return func ( d * Discover ) error { d . Providers = m return nil } }

// WithProviders allows specifying your own set of providers.

WithProviders allows specifying your own set of providers .


func (d *Discover) Names() []string {
	d.once.Do(d.initProviders)

	var names []string
	for n := range d.Providers {
		names = append(names, n)
	}
	sort.Strings(names)
	return names
}

func ( d * Discover ) Names ( ) [ ] string { d . once . Do ( d . initProviders ) var names [ ] string for n := range d . Providers { names = append ( names , n ) } sort . Strings ( names ) return names }

// Names returns the names of the configured providers.

Names returns the names of the configured providers .


func (d *Discover) Help() string {
	d.once.Do(d.initProviders)

	h := []string{globalHelp}
	for _, name := range d.Names() {
		h = append(h, d.Providers[name].Help())
	}
	return strings.Join(h, "\n")
}

func ( d * Discover ) Help ( ) string { d . once . Do ( d . initProviders ) h := [ ] string { globalHelp } for _ , name := range d . Names ( ) { h = append ( h , d . Providers [ name ] . Help ( ) ) } return strings . Join ( h , " \n " ) }

// Help describes the format of the configuration string for address discovery
// and the various provider specific options.

Help describes the format of the configuration string for address discovery and the various provider specific options .


func (d *Discover) Addrs(cfg string, l *log.Logger) ([]string, error) {
	d.once.Do(d.initProviders)

	args, err := Parse(cfg)
	if err != nil {
		return nil, fmt.Errorf("discover: %s", err)
	}

	name := args["provider"]
	if name == "" {
		return nil, fmt.Errorf("discover: no provider")
	}

	providers := d.Providers
	if providers == nil {
		providers = Providers
	}

	p := providers[name]
	if p == nil {
		return nil, fmt.Errorf("discover: unknown provider " + name)
	}
	l.Printf("[DEBUG] discover: Using provider %q", name)

	if typ, ok := p.(ProviderWithUserAgent); ok {
		typ.SetUserAgent(d.userAgent)
		return p.Addrs(args, l)
	}

	return p.Addrs(args, l)
}

func ( d * Discover ) Addrs ( cfg string , l * log . Logger ) ( [ ] string , error ) { d . once . Do ( d . initProviders ) args , err := Parse ( cfg ) if err != nil { return nil , fmt . Errorf ( " " , err ) } name := args [ " " ] if name == " " { return nil , fmt . Errorf ( " " ) } providers := d . Providers if providers == nil { providers = Providers } p := providers [ name ] if p == nil { return nil , fmt . Errorf ( " " + name ) } l . Printf ( " " , name ) if typ , ok := p . ( ProviderWithUserAgent ) ; ok { typ . SetUserAgent ( d . userAgent ) return p . Addrs ( args , l ) } return p . Addrs ( args , l ) }

// Addrs discovers ip addresses of nodes that match the given filter criteria.
// The config string must have the format 'provider=xxx key=val key=val ...'
// where the keys and values are provider specific. The values are URL encoded.

Addrs discovers ip addresses of nodes that match the given filter criteria . The config string must have the format provider = xxx key = val key = val ... where the keys and values are provider specific . The values are URL encoded .


func (c Config) String() string {
	// sort 'provider' to the front and keep the keys stable.
	var keys []string
	for k := range c {
		if k != "provider" {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)
	keys = append([]string{"provider"}, keys...)

	quote := func(s string) string {
		if strings.ContainsAny(s, ` "\`) {
			return strconv.Quote(s)
		}
		return s
	}

	var vals []string
	for _, k := range keys {
		v := c[k]
		if v == "" {
			continue
		}
		vals = append(vals, quote(k)+"="+quote(v))
	}
	return strings.Join(vals, " ")
}

// String formats a config map into the "key=val key=val ..."
// understood by Parse. The order of the keys is stable.

String formats a config map into the key = val key = val ... understood by Parse . The order of the keys is stable .


func PodAddrs(pods *corev1.PodList, args map[string]string, l *log.Logger) ([]string, error) {
	hostNetwork := false
	if v := args["host_network"]; v != "" {
		var err error
		hostNetwork, err = strconv.ParseBool(v)
		if err != nil {
			return nil, fmt.Errorf("discover-k8s: host_network must be boolean value: %s", err)
		}
	}

	var addrs []string
PodLoop:
	for _, pod := range pods.Items {
		if pod.Status.Phase != corev1.PodRunning {
			l.Printf("[DEBUG] discover-k8s: ignoring pod %q, not running: %q",
				pod.Name, pod.Status.Phase)
			continue
		}

		// If there is a Ready condition available, we need that to be true.
		// If no ready condition is set, then we accept this pod regardless.
		for _, condition := range pod.Status.Conditions {
			if condition.Type == corev1.PodReady && condition.Status != corev1.ConditionTrue {
				l.Printf("[DEBUG] discover-k8s: ignoring pod %q, not ready state", pod.Name)
				continue PodLoop
			}
		}

		// Get the IP address that we will join.
		addr := pod.Status.PodIP
		if hostNetwork {
			addr = pod.Status.HostIP
		}
		if addr == "" {
			// This can be empty according to the API docs, so we protect that.
			l.Printf("[DEBUG] discover-k8s: ignoring pod %q, requested IP is empty", pod.Name)
			continue
		}

		// We only use the port if it is specified as an annotation. The
		// annotation value can be a name or a number.
		if v := pod.Annotations[AnnotationKeyPort]; v != "" {
			port, err := podPort(&pod, v, hostNetwork)
			if err != nil {
				l.Printf("[DEBUG] discover-k8s: ignoring pod %q, error retrieving port: %s",
					pod.Name, err)
				continue
			}

			addr = fmt.Sprintf("%s:%d", addr, port)
		}

		addrs = append(addrs, addr)
	}

	return addrs, nil
}

// PodAddrs extracts the addresses from a list of pods.
//
// This is a separate method so that we can unit test this without having
// to setup complicated K8S cluster scenarios. It shouldn't generally be
// called externally.

PodAddrs extracts the addresses from a list of pods . This is a separate method so that we can unit test this without having to setup complicated K8S cluster scenarios . It shouldn t generally be called externally .


func argsOrEnv(args map[string]string, key, env string) string {
  if value, ok := args[key]; ok {
    return value
  }
  return os.Getenv(env)
}

func argsOrEnv ( args map [ string ] string , key , env string ) string { if value , ok := args [ key ] ; ok { return value } return os . Getenv ( env ) }

// argsOrEnv allows you to pick an environmental variable for a setting if the arg is not set


func (p *Provider) Addrs(args map[string]string, l *log.Logger) ([]string, error) {
	var params *m.QueryParam
	var ch chan *m.ServiceEntry
	var v6, v4 bool
	var addrs []string
	var err error

	// default to null logger
	if l == nil {
		l = log.New(ioutil.Discard, "", 0)
	}

	// init params
	params = new(m.QueryParam)

	// validate and set service record
	if args["service"] == "" {
		return nil, fmt.Errorf("discover-mdns: Service record not provided." +
			"  Please specify a service record for the mDNS lookup.")
	}
	params.Service = args["service"]

	// validate and set domain
	if args["domain"] != "" {
		params.Domain = args["domain"]
	} else {
		params.Domain = "local"
	}

	// validate and set timeout
	if args["timeout"] != "" {
		if params.Timeout, err = time.ParseDuration(args["timeout"]); err != nil {
			return nil, fmt.Errorf("discover-mdns: Failed to parse timeout: %s", err)
		}
	} else {
		params.Timeout = 5 * time.Second
	}

	// validate and set v6 toggle
	if args["v6"] != "" {
		if v6, err = strconv.ParseBool(args["v6"]); err != nil {
			return nil, fmt.Errorf("discover-mdns: Failed to parse v6: %s", err)
		}
	} else {
		v6 = true
	}

	// validate and set v4 toggle
	if args["v4"] != "" {
		if v4, err = strconv.ParseBool(args["v4"]); err != nil {
			return nil, fmt.Errorf("discover-mdns: Failed to parse v4: %s", err)
		}
	} else {
		v4 = true
	}

	// init entries channel
	ch = make(chan *m.ServiceEntry)
	defer close(ch)
	params.Entries = ch

	// build addresses
	go func() {
		var addr string
		for e := range ch {
			addr = "" // reset addr each loop
			if v6 && e.AddrV6 != nil {
				addr = net.JoinHostPort(e.AddrV6.String(),
					strconv.Itoa(e.Port))
			}
			if addr == "" && v4 && e.AddrV4 != nil {
				addr = net.JoinHostPort(e.AddrV4.String(),
					strconv.Itoa(e.Port))
			}
			if addr != "" {
				l.Printf("[DEBUG] discover-mdns: %s -> %s",
					e.Host, addr)
				// build address list
				addrs = append(addrs, addr)
			}
		}
	}()

	// lookup and return
	return addrs, m.Query(params)
}

func
(
p
*
Provider
)
Addrs
(
args
map
[
string
]
string
,
l
*
log
.
Logger
)
(
[
]
string
,
error
)
{
var
params
*
m
.
QueryParam
var
ch
chan
*
m
.
ServiceEntry
var
v6
,
v4
bool
var
addrs
[
]
string
var
err
error
// default to null logger
if
l
==
{
l
=
log
.
New
(
ioutil
.
Discard
,
"
"
,
0
)
}
// init params
params
=
new
(
m
.
QueryParam
)
// validate and set service record
if
args
[
"
"
]
==
"
"
{
return
,
fmt
.
Errorf
(
"
"
+
"
"
)
}
params
.
Service
=
args
[
"
"
]
// validate and set domain
if
args
[
"
"
]
!=
"
"
{
params
.
Domain
=
args
[
"
"
]
}
else
{
params
.
Domain
=
"
"
}
// validate and set timeout
if
args
[
"
"
]
!=
"
"
{
if
params
.
Timeout
,
err
=
time
.
ParseDuration
(
args
[
"
"
]
)
;
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
}
else
{
params
.
Timeout
=
5
*
time
.
Second
}
// validate and set v6 toggle
if
args
[
"
"
]
!=
"
"
{
if
v6
,
err
=
strconv
.
ParseBool
(
args
[
"
"
]
)
;
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
}
else
{
v6
=
true
}
// validate and set v4 toggle
if
args
[
"
"
]
!=
"
"
{
if
v4
,
err
=
strconv
.
ParseBool
(
args
[
"
"
]
)
;
err
!=
{
return
,
fmt
.
Errorf
(
"
"
,
err
)
}
}
else
{
v4
=
true
}
// init entries channel
ch
=
make
(
chan
*
m
.
ServiceEntry
)
defer
close
(
ch
)
params
.
Entries
=
ch
// build addresses
go
func
(
)
{
var
addr
string
for
e
:=
range
ch
{
addr
=
"
"
// reset addr each loop
if
v6
&&
e
.
AddrV6
!=
{
addr
=
net
.
JoinHostPort
(
e
.
AddrV6
.
String
(
)
,
strconv
.
Itoa
(
e
.
Port
)
)
}
if
addr
==
"
"
&&
v4
&&
e
.
AddrV4
!=
{
addr
=
net
.
JoinHostPort
(
e
.
AddrV4
.
String
(
)
,
strconv
.
Itoa
(
e
.
Port
)
)
}
if
addr
!=
"
"
{
l
.
Printf
(
"
"
,
e
.
Host
,
addr
)
// build address list
addrs
=
append
(
addrs
,
addr
)
}
}
}
(
)
// lookup and return
return
addrs
,
m
.
Query
(
params
)
}

// Addrs returns discovered addresses for the mDNS package.

Addrs returns discovered addresses for the mDNS package .


func setLog(l *log.Logger) {
	if l != nil {
		logger = l
	} else {
		logger = log.New(ioutil.Discard, "", 0)
	}
}

func setLog ( l * log . Logger ) { if l != nil { logger = l } else { logger = log . New ( ioutil . Discard , " " , 0 ) } }


func discoverErr(format string, a ...interface{}) error {
	var s string
	if len(a) > 1 {
		s = fmt.Sprintf(format, a...)
	} else {
		s = format
	}
	return fmt.Errorf("discover-vsphere: %s", s)
}

func discoverErr ( format string , a ... interface { } ) error { var s string if len ( a ) > 1 { s = fmt . Sprintf ( format , a ... ) } else { s = format } return fmt . Errorf ( " " , s ) }

// discoverErr prints out a friendly error heading for the top-level discovery
// errors. It should only be used in the Addrs method.

discoverErr prints out a friendly error heading for the top - level discovery errors . It should only be used in the Addrs method .


func valueOrEnv(config map[string]string, key, env string) string {
	if v := config[key]; v != "" {
		return v
	}
	if v := os.Getenv(env); v != "" {
		logger.Printf("[DEBUG] Using value of %s for configuration of %s", env, key)
		return v
	}
	return ""
}

func valueOrEnv ( config map [ string ] string , key , env string ) string { if v := config [ key ] ; v != " " { return v } if v := os . Getenv ( env ) ; v != " " { logger . Printf ( " " , env , key ) return v } return " " }

// valueOrEnv provides a way of suppling configuration values through
// environment variables. Defined values always take priority.

valueOrEnv provides a way of suppling configuration values through environment variables . Defined values always take priority .


func newVSphereClient(ctx context.Context, host, user, password string, insecure bool) (*vSphereClient, error) {
	logger.Println("[DEBUG] Connecting to vSphere client endpoints")

	client := new(vSphereClient)

	u, err := vimURL(host, user, password)
	if err != nil {
		return nil, fmt.Errorf("error generating SOAP endpoint url: %s", err)
	}

	// Set up the VIM/govmomi client connection
	client.VimClient, err = newVimSession(ctx, u, insecure)
	if err != nil {
		return nil, err
	}

	client.TagsClient, err = newRestSession(ctx, u, insecure)
	if err != nil {
		return nil, err
	}

	logger.Println("[DEBUG] All vSphere client endpoints connected successfully")
	return client, nil
}

// newVSphereClient returns a new vSphereClient after setting up the necessary
// connections.

newVSphereClient returns a new vSphereClient after setting up the necessary connections .


func newVimSession(ctx context.Context, u *url.URL, insecure bool) (*govmomi.Client, error) {
	logger.Printf("[DEBUG] Creating new SOAP API session on endpoint %s", u.Host)
	client, err := govmomi.NewClient(ctx, u, insecure)
	if err != nil {
		return nil, fmt.Errorf("error setting up new vSphere SOAP client: %s", err)
	}

	logger.Println("[DEBUG] SOAP API session creation successful")
	return client, nil
}

func newVimSession ( ctx context . Context , u * url . URL , insecure bool ) ( * govmomi . Client , error ) { logger . Printf ( " " , u . Host ) client , err := govmomi . NewClient ( ctx , u , insecure ) if err != nil { return nil , fmt . Errorf ( " " , err ) } logger . Println ( " " ) return client , nil }

// newVimSession connects the VIM SOAP API client connection.

newVimSession connects the VIM SOAP API client connection .


func newRestSession(ctx context.Context, u *url.URL, insecure bool) (*tags.RestClient, error) {
	logger.Printf("[DEBUG] Creating new CIS REST API session on endpoint %s", u.Host)
	client := tags.NewClient(u, insecure, "")
	if err := client.Login(ctx); err != nil {
		return nil, fmt.Errorf("error connecting to CIS REST endpoint: %s", err)
	}

	logger.Println("[DEBUG] CIS REST API session creation successful")
	return client, nil
}

func newRestSession ( ctx context . Context , u * url . URL , insecure bool ) ( * tags . RestClient , error ) { logger . Printf ( " " , u . Host ) client := tags . NewClient ( u , insecure , " " ) if err := client . Login ( ctx ) ; err != nil { return nil , fmt . Errorf ( " " , err ) } logger . Println ( " " ) return client , nil }

// newRestSession connects to the vSphere REST API endpoint, necessary for
// tags.

newRestSession connects to the vSphere REST API endpoint necessary for tags .


func (p *Provider) Addrs(args map[string]string, l *log.Logger) ([]string, error) {
	if args["provider"] != "vsphere" {
		return nil, discoverErr("invalid provider %s", args["provider"])
	}

	setLog(l)

	tagName := args["tag_name"]
	categoryName := args["category_name"]
	host := valueOrEnv(args, "host", "VSPHERE_SERVER")
	user := valueOrEnv(args, "user", "VSPHERE_USER")
	password := valueOrEnv(args, "password", "VSPHERE_PASSWORD")
	insecure, err := strconv.ParseBool(valueOrEnv(args, "insecure_ssl", "VSPHERE_ALLOW_UNVERIFIED_SSL"))
	if err != nil {
		logger.Println("[DEBUG] Non-truthy/falsey value for insecure_ssl, assuming false")
	}
	timeout, err := time.ParseDuration(args["timeout"])
	if err != nil {
		logger.Println("[DEBUG] Non-time value given for timeout, assuming 10m")
		timeout = time.Minute * 10
	}

	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	client, err := newVSphereClient(ctx, host, user, password, insecure)
	if err != nil {
		return nil, discoverErr(err.Error())
	}

	if tagName == "" || categoryName == "" {
		return nil, discoverErr("both tag_name and category_name must be specified")
	}

	logger.Printf("[INFO] Locating all virtual machine IP addresses with tag %q in category %q", tagName, categoryName)

	tagID, err := tagIDFromName(ctx, client.TagsClient, tagName, categoryName)
	if err != nil {
		return nil, discoverErr(err.Error())
	}

	addrs, err := virtualMachineIPsForTag(ctx, client, tagID)
	if err != nil {
		return nil, discoverErr(err.Error())
	}

	logger.Printf("[INFO] Final IP address list: %s", strings.Join(addrs, ","))
	return addrs, nil
}

func ( p * Provider ) Addrs ( args map [ string ] string , l * log . Logger ) ( [ ] string , error ) { if args [ " " ] != " " { return nil , discoverErr ( " " , args [ " " ] ) } setLog ( l ) tagName := args [ " " ] categoryName := args [ " " ] host := valueOrEnv ( args , " " , " " ) user := valueOrEnv ( args , " " , " " ) password := valueOrEnv ( args , " " , " " ) insecure , err := strconv . ParseBool ( valueOrEnv ( args , " " , " " ) ) if err != nil { logger . Println ( " " ) } timeout , err := time . ParseDuration ( args [ " " ] ) if err != nil { logger . Println ( " " ) timeout = time . Minute * 10 } ctx , cancel := context . WithTimeout ( context . Background ( ) , timeout ) defer cancel ( ) client , err := newVSphereClient ( ctx , host , user , password , insecure ) if err != nil { return nil , discoverErr ( err . Error ( ) ) } if tagName == " " || categoryName == " " { return nil , discoverErr ( " " ) } logger . Printf ( " " , tagName , categoryName ) tagID , err := tagIDFromName ( ctx , client . TagsClient , tagName , categoryName ) if err != nil { return nil , discoverErr ( err . Error ( ) ) } addrs , err := virtualMachineIPsForTag ( ctx , client , tagID ) if err != nil { return nil , discoverErr ( err . Error ( ) ) } logger . Printf ( " " , strings . Join ( addrs , " " ) ) return addrs , nil }

// Addrs implements the Provider interface for the vsphere package.

Addrs implements the Provider interface for the vsphere package .


func tagIDFromName(ctx context.Context, client *tags.RestClient, name, category string) (string, error) {
	logger.Printf("[DEBUG] Fetching tag ID for tag name %q and category %q", name, category)

	categoryID, err := tagCategoryByName(ctx, client, category)
	if err != nil {
		return "", err
	}

	return tagByName(ctx, client, name, categoryID)
}

func tagIDFromName ( ctx context . Context , client * tags . RestClient , name , category string ) ( string , error ) { logger . Printf ( " " , name , category ) categoryID , err := tagCategoryByName ( ctx , client , category ) if err != nil { return " " , err } return tagByName ( ctx , client , name , categoryID ) }

// tagIDFromName helps convert the tag and category names into the final ID
// used for discovery.

tagIDFromName helps convert the tag and category names into the final ID used for discovery .


func tagCategoryByName(ctx context.Context, client *tags.RestClient, name string) (string, error) {
	cats, err := client.GetCategoriesByName(ctx, name)
	if err != nil {
		return "", fmt.Errorf("could not get category for name %q: %s", name, err)
	}

	if len(cats) < 1 {
		return "", fmt.Errorf("category name %q not found", name)
	}
	if len(cats) > 1 {
		// Although GetCategoriesByName does not seem to think that tag categories
		// are unique, empirical observation via the console and API show that they
		// are. This error case is handled anyway.
		return "", fmt.Errorf("multiple categories with name %q found", name)
	}

	return cats[0].ID, nil
}

// tagCategoryByName converts a tag category name into its ID.

tagCategoryByName converts a tag category name into its ID .


func tagByName(ctx context.Context, client *tags.RestClient, name, categoryID string) (string, error) {
	tids, err := client.GetTagByNameForCategory(ctx, name, categoryID)
	if err != nil {
		return "", fmt.Errorf("could not get tag for name %q: %s", name, err)
	}

	if len(tids) < 1 {
		return "", fmt.Errorf("tag name %q not found in category ID %q", name, categoryID)
	}
	if len(tids) > 1 {
		// This situation is very similar to the one in tagCategoryByName. The API
		// docs even say that tags need to be unique in categories, yet
		// GetTagByNameForCategory still returns multiple results.
		return "", fmt.Errorf("multiple tags with name %q found", name)
	}

	logger.Printf("[DEBUG] Tag ID is %q", tids[0].ID)
	return tids[0].ID, nil
}

// tagByName converts a tag name into its ID.

tagByName converts a tag name into its ID .


func virtualMachineIPsForTag(ctx context.Context, client *vSphereClient, id string) ([]string, error) {
	vms, err := virtualMachinesForTag(ctx, client, id)
	if err != nil {
		return nil, err
	}

	return ipAddrsForVirtualMachines(ctx, client, vms)
}

func virtualMachineIPsForTag ( ctx context . Context , client * vSphereClient , id string ) ( [ ] string , error ) { vms , err := virtualMachinesForTag ( ctx , client , id ) if err != nil { return nil , err } return ipAddrsForVirtualMachines ( ctx , client , vms ) }

// virtualMachineIPsForTag is a higher-level wrapper that calls out to
// functions to fetch all of the virtual machines matching a certain tag ID,
// and then gets all of the IP addresses for those virtual machines.

virtualMachineIPsForTag is a higher - level wrapper that calls out to functions to fetch all of the virtual machines matching a certain tag ID and then gets all of the IP addresses for those virtual machines .


func virtualMachinesForTag(ctx context.Context, client *vSphereClient, id string) ([]*object.VirtualMachine, error) {
	logger.Printf("[DEBUG] Locating all virtual machines under tag ID %q", id)

	var vms []*object.VirtualMachine

	objs, err := client.TagsClient.ListAttachedObjects(ctx, id)
	if err != nil {
		return nil, err
	}
	for i, obj := range objs {
		switch {
		case obj.Type == nil || obj.ID == nil:
			logger.Printf("[WARN] Discovered object at index %d has either no ID or type", i)
			continue
		case *obj.Type != "VirtualMachine":
			logger.Printf("[DEBUG] Discovered object ID %q is not a virutal machine", *obj.ID)
			continue
		}
		vm, err := virtualMachineFromMOID(ctx, client.VimClient, *obj.ID)
		if err != nil {
			return nil, fmt.Errorf("error locating virtual machine with ID %q: %s", *obj.ID, err)
		}
		vms = append(vms, vm)
	}

	logger.Printf("[DEBUG] Discovered virtual machines: %s", virtualMachineNames(vms))
	return vms, nil
}

func virtualMachinesForTag ( ctx context . Context , client * vSphereClient , id string ) ( [ ] * object . VirtualMachine , error ) { logger . Printf ( " " , id ) var vms [ ] * object . VirtualMachine objs , err := client . TagsClient . ListAttachedObjects ( ctx , id ) if err != nil { return nil , err } for i , obj := range objs { switch { case obj . Type == nil || obj . ID == nil : logger . Printf ( " " , i ) continue case * obj . Type != " " : logger . Printf ( " " , * obj . ID ) continue } vm , err := virtualMachineFromMOID ( ctx , client . VimClient , * obj . ID ) if err != nil { return nil , fmt . Errorf ( " " , * obj . ID , err ) } vms = append ( vms , vm ) } logger . Printf ( " " , virtualMachineNames ( vms ) ) return vms , nil }

// virtualMachinesForTag discovers all of the virtual machines that match a
// specific tag ID and returns their higher level helper objects.

virtualMachinesForTag discovers all of the virtual machines that match a specific tag ID and returns their higher level helper objects .


func ipAddrsForVirtualMachines(ctx context.Context, client *vSphereClient, vms []*object.VirtualMachine) ([]string, error) {
	var addrs []string
	for _, vm := range vms {
		as, err := buildAndSelectGuestIPs(ctx, vm)
		if err != nil {
			return nil, err
		}
		addrs = append(addrs, as...)
	}
	return addrs, nil
}

func ipAddrsForVirtualMachines ( ctx context . Context , client * vSphereClient , vms [ ] * object . VirtualMachine ) ( [ ] string , error ) { var addrs [ ] string for _ , vm := range vms { as , err := buildAndSelectGuestIPs ( ctx , vm ) if err != nil { return nil , err } addrs = append ( addrs , as ... ) } return addrs , nil }

// ipAddrsForVirtualMachines takes a set of virtual machines and returns a
// consolidated list of IP addresses for all of the VMs.

ipAddrsForVirtualMachines takes a set of virtual machines and returns a consolidated list of IP addresses for all of the VMs .


func virtualMachineFromMOID(ctx context.Context, client *govmomi.Client, id string) (*object.VirtualMachine, error) {
	logger.Printf("[DEBUG] Locating VM with managed object ID %q", id)

	finder := find.NewFinder(client.Client, false)

	ref := types.ManagedObjectReference{
		Type:  "VirtualMachine",
		Value: id,
	}

	vm, err := finder.ObjectReference(ctx, ref)
	if err != nil {
		return nil, err
	}
	// Should be safe to return here. If our reference returned here and is not a
	// VM, then we have bigger problems and to be honest we should be panicking
	// anyway.
	return vm.(*object.VirtualMachine), nil
}

// virtualMachineFromMOID locates a virtual machine by its managed object
// reference ID.

virtualMachineFromMOID locates a virtual machine by its managed object reference ID .


func virtualMachineProperties(ctx context.Context, vm *object.VirtualMachine, keys []string) (*mo.VirtualMachine, error) {
	logger.Printf("[DEBUG] Fetching properties for VM %q", vm.Name())
	var props mo.VirtualMachine
	if err := vm.Properties(ctx, vm.Reference(), keys, &props); err != nil {
		return nil, err
	}
	return &props, nil
}

func virtualMachineProperties ( ctx context . Context , vm * object . VirtualMachine , keys [ ] string ) ( * mo . VirtualMachine , error ) { logger . Printf ( " " , vm . Name ( ) ) var props mo . VirtualMachine if err := vm . Properties ( ctx , vm . Reference ( ) , keys , & props ) ; err != nil { return nil , err } return & props , nil }

// virtualMachineProperties is a convenience method that wraps fetching the
// VirtualMachine MO from its higher-level object.
//
// It takes a list of property keys to fetch. Keeping the property set small
// can sometimes result in significant performance improvements.

virtualMachineProperties is a convenience method that wraps fetching the VirtualMachine MO from its higher - level object . It takes a list of property keys to fetch . Keeping the property set small can sometimes result in significant performance improvements .


func buildAndSelectGuestIPs(ctx context.Context, vm *object.VirtualMachine) ([]string, error) {
	logger.Printf("[DEBUG] Discovering addresses for virtual machine %q", vm.Name())
	var addrs []string

	props, err := virtualMachineProperties(ctx, vm, []string{"guest.net"})
	if err != nil {
		return nil, fmt.Errorf("cannot fetch properties for VM %q: %s", vm.Name(), err)
	}

	if props.Guest == nil || props.Guest.Net == nil {
		logger.Printf("[WARN] No networking stack information available for %q or VMware tools not running", vm.Name())
		return nil, nil
	}

	// Now fetch all IP addresses, checking at the same time to see if the IP
	// address is eligible to be a primary IP address.
	for _, n := range props.Guest.Net {
		if n.IpConfig != nil {
			for _, addr := range n.IpConfig.IpAddress {
				if skipIPAddr(net.ParseIP(addr.IpAddress)) {
					continue
				}
				addrs = append(addrs, addr.IpAddress)
			}
		}
	}

	logger.Printf("[INFO] Discovered IP addresses for virtual machine %q: %s", vm.Name(), strings.Join(addrs, ","))
	return addrs, nil
}

// buildAndSelectGuestIPs builds a list of IP addresses known to VMware tools,
// skipping local and auto-configuration addresses.
//
// The builder is non-discriminate and is only deterministic to the order that
// it discovers addresses in VMware tools.

buildAndSelectGuestIPs builds a list of IP addresses known to VMware tools skipping local and auto - configuration addresses . The builder is non - discriminate and is only deterministic to the order that it discovers addresses in VMware tools .


func skipIPAddr(ip net.IP) bool {
	switch {
	case ip.IsLinkLocalMulticast():
		fallthrough
	case ip.IsLinkLocalUnicast():
		fallthrough
	case ip.IsLoopback():
		fallthrough
	case ip.IsMulticast():
		return true
	}
	return false
}

func skipIPAddr ( ip net . IP ) bool { switch { case ip . IsLinkLocalMulticast ( ) : fallthrough case ip . IsLinkLocalUnicast ( ) : fallthrough case ip . IsLoopback ( ) : fallthrough case ip . IsMulticast ( ) : return true } return false }

// skipIPAddr defines the set of criteria that buildAndSelectGuestIPs uses to
// check to see if it needs to skip an IP address.

skipIPAddr defines the set of criteria that buildAndSelectGuestIPs uses to check to see if it needs to skip an IP address .


func virtualMachineNames(vms []*object.VirtualMachine) string {
	var s []string
	for _, vm := range vms {
		s = append(s, vm.Name())
	}
	return strings.Join(s, ",")
}

func virtualMachineNames ( vms [ ] * object . VirtualMachine ) string { var s [ ] string for _ , vm := range vms { s = append ( s , vm . Name ( ) ) } return strings . Join ( s , " " ) }

// virtualMachineNames is a helper method that returns all the names for a list
// of virtual machines, comma separated.

virtualMachineNames is a helper method that returns all the names for a list of virtual machines comma separated .


func validateAndWrapHandler(h Handler) Handler {
	if reflect.TypeOf(h).Kind() != reflect.Func {
		panic("Macaron handler must be a callable function")
	}

	if !inject.IsFastInvoker(h) {
		switch v := h.(type) {
		case func(*Context):
			return ContextInvoker(v)
		case func(*Context, *log.Logger):
			return LoggerInvoker(v)
		case func(http.ResponseWriter, *http.Request):
			return handlerFuncInvoker(v)
		case func(http.ResponseWriter, error):
			return internalServerErrorInvoker(v)
		}
	}
	return h
}

func validateAndWrapHandler ( h Handler ) Handler { if reflect . TypeOf ( h ) . Kind ( ) != reflect . Func { panic ( " " ) } if ! inject . IsFastInvoker ( h ) { switch v := h . ( type ) { case func ( * Context ) : return ContextInvoker ( v ) case func ( * Context , * log . Logger ) : return LoggerInvoker ( v ) case func ( http . ResponseWriter , * http . Request ) : return handlerFuncInvoker ( v ) case func ( http . ResponseWriter , error ) : return internalServerErrorInvoker ( v ) } } return h }

// validateAndWrapHandler makes sure a handler is a callable function, it panics if not.
// When the handler is also potential to be any built-in inject.FastInvoker,
// it wraps the handler automatically to have some performance gain.

validateAndWrapHandler makes sure a handler is a callable function it panics if not . When the handler is also potential to be any built - in inject . FastInvoker it wraps the handler automatically to have some performance gain .


func validateAndWrapHandlers(handlers []Handler, wrappers ...func(Handler) Handler) []Handler {
	var wrapper func(Handler) Handler
	if len(wrappers) > 0 {
		wrapper = wrappers[0]
	}

	wrappedHandlers := make([]Handler, len(handlers))
	for i, h := range handlers {
		h = validateAndWrapHandler(h)
		if wrapper != nil && !inject.IsFastInvoker(h) {
			h = wrapper(h)
		}
		wrappedHandlers[i] = h
	}

	return wrappedHandlers
}

func validateAndWrapHandlers ( handlers [ ] Handler , wrappers ... func ( Handler ) Handler ) [ ] Handler { var wrapper func ( Handler ) Handler if len ( wrappers ) > 0 { wrapper = wrappers [ 0 ] } wrappedHandlers := make ( [ ] Handler , len ( handlers ) ) for i , h := range handlers { h = validateAndWrapHandler ( h ) if wrapper != nil && ! inject . IsFastInvoker ( h ) { h = wrapper ( h ) } wrappedHandlers [ i ] = h } return wrappedHandlers }

// validateAndWrapHandlers preforms validation and wrapping for each input handler.
// It accepts an optional wrapper function to perform custom wrapping on handlers.

validateAndWrapHandlers preforms validation and wrapping for each input handler . It accepts an optional wrapper function to perform custom wrapping on handlers .


func NewWithLogger(out io.Writer) *Macaron {
	m := &Macaron{
		Injector: inject.New(),
		action:   func() {},
		Router:   NewRouter(),
		logger:   log.New(out, "[Macaron] ", 0),
	}
	m.Router.m = m
	m.Map(m.logger)
	m.Map(defaultReturnHandler())
	m.NotFound(http.NotFound)
	m.InternalServerError(func(rw http.ResponseWriter, err error) {
		http.Error(rw, err.Error(), 500)
	})
	return m
}

func NewWithLogger ( out io . Writer ) * Macaron { m := & Macaron { Injector : inject . New ( ) , action : func ( ) { } , Router : NewRouter ( ) , logger : log . New ( out , " " , 0 ) , } m . Router . m = m m . Map ( m . logger ) m . Map ( defaultReturnHandler ( ) ) m . NotFound ( http . NotFound ) m . InternalServerError ( func ( rw http . ResponseWriter , err error ) { http . Error ( rw , err . Error ( ) , 500 ) } ) return m }

// NewWithLogger creates a bare bones Macaron instance.
// Use this method if you want to have full control over the middleware that is used.
// You can specify logger output writer with this function.

NewWithLogger creates a bare bones Macaron instance . Use this method if you want to have full control over the middleware that is used . You can specify logger output writer with this function .


func (m *Macaron) Handlers(handlers ...Handler) {
	m.handlers = make([]Handler, 0)
	for _, handler := range handlers {
		m.Use(handler)
	}
}

func ( m * Macaron ) Handlers ( handlers ... Handler ) { m . handlers = make ( [ ] Handler , 0 ) for _ , handler := range handlers { m . Use ( handler ) } }

// Handlers sets the entire middleware stack with the given Handlers.
// This will clear any current middleware handlers,
// and panics if any of the handlers is not a callable function

Handlers sets the entire middleware stack with the given Handlers . This will clear any current middleware handlers and panics if any of the handlers is not a callable function


func (m *Macaron) Use(handler Handler) {
	handler = validateAndWrapHandler(handler)
	m.handlers = append(m.handlers, handler)
}

func ( m * Macaron ) Use ( handler Handler ) { handler = validateAndWrapHandler ( handler ) m . handlers = append ( m . handlers , handler ) }

// Use adds a middleware Handler to the stack,
// and panics if the handler is not a callable func.
// Middleware Handlers are invoked in the order that they are added.

Use adds a middleware Handler to the stack and panics if the handler is not a callable func . Middleware Handlers are invoked in the order that they are added .


func (m *Macaron) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	if m.hasURLPrefix {
		req.URL.Path = strings.TrimPrefix(req.URL.Path, m.urlPrefix)
	}
	for _, h := range m.befores {
		if h(rw, req) {
			return
		}
	}
	m.Router.ServeHTTP(rw, req)
}

func ( m * Macaron ) ServeHTTP ( rw http . ResponseWriter , req * http . Request ) { if m . hasURLPrefix { req . URL . Path = strings . TrimPrefix ( req . URL . Path , m . urlPrefix ) } for _ , h := range m . befores { if h ( rw , req ) { return } } m . Router . ServeHTTP ( rw , req ) }

// ServeHTTP is the HTTP Entry point for a Macaron instance.
// Useful if you want to control your own HTTP server.
// Be aware that none of middleware will run without registering any router.

ServeHTTP is the HTTP Entry point for a Macaron instance . Useful if you want to control your own HTTP server . Be aware that none of middleware will run without registering any router .


func (m *Macaron) SetURLPrefix(prefix string) {
	m.urlPrefix = prefix
	m.hasURLPrefix = len(m.urlPrefix) > 0
}

func ( m * Macaron ) SetURLPrefix ( prefix string ) { m . urlPrefix = prefix m . hasURLPrefix = len ( m . urlPrefix ) > 0 }

// SetURLPrefix sets URL prefix of router layer, so that it support suburl.

SetURLPrefix sets URL prefix of router layer so that it support suburl .