<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package maas

import (
	"fmt"
	"strings"

	"github.com/juju/errors"
	"launchpad.net/gomaasapi"

	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
)

type maasInstance struct {
	maasObject *gomaasapi.MAASObject
}

var _ instance.Instance = (*maasInstance)(nil)

func (mi *maasInstance) String() string {
	hostname, err := mi.hostname()
	if err != nil {
		// This is meant to be impossible, but be paranoid.
		hostname = fmt.Sprintf("<DNSName failed: %q>", err)
	}
	return fmt.Sprintf("%s:%s", hostname, mi.Id())
}

func (mi *maasInstance) Id() instance.Id {
	return maasObjectId(mi.maasObject)
}

func maasObjectId(maasObject *gomaasapi.MAASObject) instance.Id {
	// Use the node's 'resource_uri' value.
	return instance.Id(maasObject.URI().String())
}

func (mi *maasInstance) Status() string {
	// MAAS does not track node status once they're allocated.
	// Since any instance that juju knows about will be an
	// allocated one, it doesn't make sense to report any
	// state unless we obtain it through some means other than
	// through the MAAS API.
	return ""
}

func (mi *maasInstance) Addresses() ([]network.Address, error) {
	interfaceAddresses, err := mi.interfaceAddresses()
	if errors.IsNotSupported(err) {
		logger.Warningf("cannot get interface addresses (using legacy approach)")
		return mi.legacyAddresses()
	} else if err != nil {
		return nil, errors.Annotate(err, "getting node interfaces")
	}

	logger.Debugf("instance %q has interface addresses: %+v", mi.Id(), interfaceAddresses)
	return interfaceAddresses, nil
}

func (mi *maasInstance) legacyAddresses() ([]network.Address, error) {
	name, err := mi.hostname()
	if err != nil {
		return nil, err
	}
	// MAAS prefers to use the dns name for intra-node communication.
	// When Juju looks up the address to use for communicating between
	// nodes, it looks up the address by scope. So we add a cloud
	// local address for that purpose.
	addrs := network.NewAddresses(name, name)
	addrs[0].Scope = network.ScopePublic
	addrs[1].Scope = network.ScopeCloudLocal

	// Append any remaining IP addresses after the preferred ones.
	ips, err := mi.ipAddresses()
	if err != nil {
		return nil, err
	}
	addrs = append(addrs, network.NewAddresses(ips...)...)

	// Although we would prefer a DNS name there's no point
	// returning unresolvable names because activities like 'juju
	// ssh 0' will instantly fail.
	return network.ResolvableHostnames(addrs), nil
}

// TODO(dimitern): In a follow-up, reuse maasObjectNetworkInterfaces to extract
// the addresses below.
//
// LKK Card: https://canonical.leankit.com/Boards/View/101652562/119311417
func (mi *maasInstance) interfaceAddresses() ([]network.Address, error) {
	// Fetch a fresh copy of the instance JSON first.
	obj, err := mi.maasObject.Get()
	if err != nil {
		return nil, errors.Annotate(err, "getting instance details")
	}
	// Extract the "interface_set" list, and process all the links of each
	// interface to get the mapping between assigned address and the space it
	// belongs to.
	interfacesArray := obj.GetMap()["interface_set"]
	if interfacesArray.IsNil() {
		// Older MAAS versions do not return interface_set.
		return nil, errors.NotSupportedf("interface_set")
	}
	objs, err := interfacesArray.GetArray()
	if err != nil {
		return nil, errors.Annotate(err, "getting interfaces list")
	}
	var addresses []network.Address
	for i, obj := range objs {
		objMap, err := obj.GetMap()
		if err != nil {
			return nil, errors.Annotate(err, "getting interface map")
		}
		nameField, ok := objMap["name"]
		if !ok || nameField.IsNil() {
			return nil, errors.Errorf("expected a name for interface #%d, got nothing", i)
		}
		name, err := nameField.GetString()
		if err != nil {
			return nil, errors.Annotatef(err, "expected interface #%d name as string", i)
		}
		linksArray, ok := objMap["links"]
		if !ok || linksArray.IsNil() {
			logger.Warningf("skipping interface %#d %q with no links", i, name)
			continue
		}
		links, err := linksArray.GetArray()
		if err != nil {
			return nil, errors.Annotatef(err, "getting interface #%d %q links", i, name)
		}<commit_msg>Fixed a go vet issue<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package maas

import (
	"fmt"
	"strings"

	"github.com/juju/errors"
	"launchpad.net/gomaasapi"

	"github.com/juju/juju/instance"
	"github.com/juju/juju/network"
)

type maasInstance struct {
	maasObject *gomaasapi.MAASObject
}

var _ instance.Instance = (*maasInstance)(nil)

func (mi *maasInstance) String() string {
	hostname, err := mi.hostname()
	if err != nil {
		// This is meant to be impossible, but be paranoid.
		hostname = fmt.Sprintf("<DNSName failed: %q>", err)
	}
	return fmt.Sprintf("%s:%s", hostname, mi.Id())
}

func (mi *maasInstance) Id() instance.Id {
	return maasObjectId(mi.maasObject)
}

func maasObjectId(maasObject *gomaasapi.MAASObject) instance.Id {
	// Use the node's 'resource_uri' value.
	return instance.Id(maasObject.URI().String())
}

func (mi *maasInstance) Status() string {
	// MAAS does not track node status once they're allocated.
	// Since any instance that juju knows about will be an
	// allocated one, it doesn't make sense to report any
	// state unless we obtain it through some means other than
	// through the MAAS API.
	return ""
}

func (mi *maasInstance) Addresses() ([]network.Address, error) {
	interfaceAddresses, err := mi.interfaceAddresses()
	if errors.IsNotSupported(err) {
		logger.Warningf("cannot get interface addresses (using legacy approach)")
		return mi.legacyAddresses()
	} else if err != nil {
		return nil, errors.Annotate(err, "getting node interfaces")
	}

	logger.Debugf("instance %q has interface addresses: %+v", mi.Id(), interfaceAddresses)
	return interfaceAddresses, nil
}

func (mi *maasInstance) legacyAddresses() ([]network.Address, error) {
	name, err := mi.hostname()
	if err != nil {
		return nil, err
	}
	// MAAS prefers to use the dns name for intra-node communication.
	// When Juju looks up the address to use for communicating between
	// nodes, it looks up the address by scope. So we add a cloud
	// local address for that purpose.
	addrs := network.NewAddresses(name, name)
	addrs[0].Scope = network.ScopePublic
	addrs[1].Scope = network.ScopeCloudLocal

	// Append any remaining IP addresses after the preferred ones.
	ips, err := mi.ipAddresses()
	if err != nil {
		return nil, err
	}
	addrs = append(addrs, network.NewAddresses(ips...)...)

	// Although we would prefer a DNS name there's no point
	// returning unresolvable names because activities like 'juju
	// ssh 0' will instantly fail.
	return network.ResolvableHostnames(addrs), nil
}

// TODO(dimitern): In a follow-up, reuse maasObjectNetworkInterfaces to extract
// the addresses below.
//
// LKK Card: https://canonical.leankit.com/Boards/View/101652562/119311417
func (mi *maasInstance) interfaceAddresses() ([]network.Address, error) {
	// Fetch a fresh copy of the instance JSON first.
	obj, err := mi.maasObject.Get()
	if err != nil {
		return nil, errors.Annotate(err, "getting instance details")
	}
	// Extract the "interface_set" list, and process all the links of each
	// interface to get the mapping between assigned address and the space it
	// belongs to.
	interfacesArray := obj.GetMap()["interface_set"]
	if interfacesArray.IsNil() {
		// Older MAAS versions do not return interface_set.
		return nil, errors.NotSupportedf("interface_set")
	}
	objs, err := interfacesArray.GetArray()
	if err != nil {
		return nil, errors.Annotate(err, "getting interfaces list")
	}
	var addresses []network.Address
	for i, obj := range objs {
		objMap, err := obj.GetMap()
		if err != nil {
			return nil, errors.Annotate(err, "getting interface map")
		}
		nameField, ok := objMap["name"]
		if !ok || nameField.IsNil() {
			return nil, errors.Errorf("expected a name for interface #%d, got nothing", i)
		}
		name, err := nameField.GetString()
		if err != nil {
			return nil, errors.Annotatef(err, "expected interface #%d name as string", i)
		}
		linksArray, ok := objMap["links"]
		if !ok || linksArray.IsNil() {
			logger.Warningf("skipping interface #%d %q with no links", i, name)
			continue
		}
		links, err := linksArray.GetArray()
		if err != nil {
			return nil, errors.Annotatef(err, "getting interface #%d %q links", i, name)
		}
{'fraction_non_alphanumeric': 0.12486672195237532, 'fraction_numerical': 0.006160407534652293, 'mean_word_length': 3.9864146485528646, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6168820', 'n_tokens_mistral': 2897, 'n_tokens_neox': 2727, 'n_words': 906}