<commit_before>//go:build go1.7
// +build go1.7

package ini

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestValidDataFiles(t *testing.T) {
	const expectedFileSuffix = "_expected"
	filepath.Walk("./testdata/valid", func(path string, info os.FileInfo, err error) error {
		if strings.HasSuffix(path, expectedFileSuffix) {
			return nil
		}

		if info.IsDir() {
			return nil
		}

		f, err := os.Open(path)
		if err != nil {
			t.Errorf("%s: unexpected error, %v", path, err)
		}
		defer f.Close()

		tree, err := ParseAST(f)
		if err != nil {
			t.Errorf("%s: unexpected parse error, %v", path, err)
		}

		v := NewDefaultVisitor()
		err = Walk(tree, v)
		if err != nil {
			t.Errorf("%s: unexpected walk error, %v", path, err)
		}

		expectedPath := path + "_expected"
		e := map[string]interface{}{}

		b, err := ioutil.ReadFile(expectedPath)
		if err != nil {
			// ignore files that do not have an expected file
			return nil
		}

		err = json.Unmarshal(b, &e)
		if err != nil {
			t.Errorf("unexpected error during deserialization, %v", err)
		}

		for profile, tableIface := range e {
			p, ok := v.Sections.GetSection(profile)
			if !ok {
				t.Fatal("could not find profile " + profile)
			}

			table := tableIface.(map[string]interface{})
			for k, v := range table {
				switch e := v.(type) {
				case string:
					a := p.String(k)
					if e != a {
						t.Errorf("%s: expected %v, but received %v for profile %v", path, e, a, profile)
					}
				case int:
					a := p.Int(k)
					if int64(e) != a {
						t.Errorf("%s: expected %v, but received %v", path, e, a)
					}
				case float64:
					v := p.values[k]
					if v.Type == IntegerType {
						a := p.Int(k)
						if int64(e) != a {
							t.Errorf("%s: expected %v, but received %v", path, e, a)
						}
					} else {
						a := p.Float64(k)
						if e != a {
							t.Errorf("%s: expected %v, but received %v", path, e, a)
						}
					}
				default:
					t.Errorf("unexpected type: %T", e)
				}
			}
		}

		return nil
	})
}

func TestInvalidDataFiles(t *testing.T) {
	cases := []struct {
		path               string
		expectedParseError bool
		expectedWalkError  bool
	}{
		{
			path:               "./testdata/invalid/bad_syntax_1",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/bad_syntax_2",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/incomplete_section_profile",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/syntax_error_comment",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/invalid_keys",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/bad_section_name",
			expectedParseError: true,
		},
	}

	for i, c := range cases {
		t.Run(c.path, func(t *testing.T) {
			f, err := os.Open(c.path)
			if err != nil {
				t.Errorf("unexpected error, %v", err)
			}
			defer f.Close()

			tree, err := ParseAST(f)
			if err != nil && !c.expectedParseError {
				t.Errorf("%d: unexpected error, %v", i+1, err)
			} else if err == nil && c.expectedParseError {
				t.Errorf("%d: expected error, but received none", i+1)
			}

			if c.expectedParseError {
				return
			}

			v := NewDefaultVisitor()
			err = Walk(tree, v)
			if err == nil && c.expectedWalkError {
				t.Errorf("%d: expected error, but received none", i+1)
			}
		})
	}
}
<commit_msg>internal/ini: fix dropped test error (#4306)<commit_after>//go:build go1.7
// +build go1.7

package ini

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestValidDataFiles(t *testing.T) {
	const expectedFileSuffix = "_expected"
	filepath.Walk("./testdata/valid", func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		if strings.HasSuffix(path, expectedFileSuffix) {
			return nil
		}

		if info.IsDir() {
			return nil
		}

		f, err := os.Open(path)
		if err != nil {
			t.Errorf("%s: unexpected error, %v", path, err)
		}
		defer f.Close()

		tree, err := ParseAST(f)
		if err != nil {
			t.Errorf("%s: unexpected parse error, %v", path, err)
		}

		v := NewDefaultVisitor()
		err = Walk(tree, v)
		if err != nil {
			t.Errorf("%s: unexpected walk error, %v", path, err)
		}

		expectedPath := path + "_expected"
		e := map[string]interface{}{}

		b, err := ioutil.ReadFile(expectedPath)
		if err != nil {
			// ignore files that do not have an expected file
			return nil
		}

		err = json.Unmarshal(b, &e)
		if err != nil {
			t.Errorf("unexpected error during deserialization, %v", err)
		}

		for profile, tableIface := range e {
			p, ok := v.Sections.GetSection(profile)
			if !ok {
				t.Fatal("could not find profile " + profile)
			}

			table := tableIface.(map[string]interface{})
			for k, v := range table {
				switch e := v.(type) {
				case string:
					a := p.String(k)
					if e != a {
						t.Errorf("%s: expected %v, but received %v for profile %v", path, e, a, profile)
					}
				case int:
					a := p.Int(k)
					if int64(e) != a {
						t.Errorf("%s: expected %v, but received %v", path, e, a)
					}
				case float64:
					v := p.values[k]
					if v.Type == IntegerType {
						a := p.Int(k)
						if int64(e) != a {
							t.Errorf("%s: expected %v, but received %v", path, e, a)
						}
					} else {
						a := p.Float64(k)
						if e != a {
							t.Errorf("%s: expected %v, but received %v", path, e, a)
						}
					}
				default:
					t.Errorf("unexpected type: %T", e)
				}
			}
		}

		return nil
	})
}

func TestInvalidDataFiles(t *testing.T) {
	cases := []struct {
		path               string
		expectedParseError bool
		expectedWalkError  bool
	}{
		{
			path:               "./testdata/invalid/bad_syntax_1",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/bad_syntax_2",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/incomplete_section_profile",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/syntax_error_comment",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/invalid_keys",
			expectedParseError: true,
		},
		{
			path:               "./testdata/invalid/bad_section_name",
			expectedParseError: true,
		},
	}

	for i, c := range cases {
		t.Run(c.path, func(t *testing.T) {
			f, err := os.Open(c.path)
			if err != nil {
				t.Errorf("unexpected error, %v", err)
			}
			defer f.Close()

			tree, err := ParseAST(f)
			if err != nil && !c.expectedParseError {
				t.Errorf("%d: unexpected error, %v", i+1, err)
			} else if err == nil && c.expectedParseError {
				t.Errorf("%d: expected error, but received none", i+1)
			}

			if c.expectedParseError {
				return
			}

			v := NewDefaultVisitor()
			err = Walk(tree, v)
			if err == nil && c.expectedWalkError {
				t.Errorf("%d: expected error, but received none", i+1)
			}
		})
	}
}
<|endoftext|>


<commit_before>package roaming

import (
	"encoding/hex"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/brocaar/chirpstack-network-server/v3/internal/config"
	"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
)

// ErrNoAgreement is returned when the requested agreement could not be found.
var ErrNoAgreement = errors.New("agreement not found")

type agreement struct {
	netID                  lorawan.NetID
	passiveRoaming         bool
	passiveRoamingLifetime time.Duration
	passiveRoamingKEKLabel string
	server                 string
	client                 backend.Client
}

var (
	resolveNetIDDomainSuffix string
	roamingEnabled           bool
	netID                    lorawan.NetID
	agreements               []agreement
	keks                     map[string][]byte

	defaultEnabled                bool
	defaultPassiveRoaming         bool
	defaultPassiveRoamingLifetime time.Duration
	defaultPassiveRoamingKEKLabel string
	defaultAsync                  bool
	defaultAsyncTimeout           time.Duration
	defaultServer                 string
	defaultCACert                 string
	defaultTLSCert                string
	defaultTLSKey                 string
	defaultAuthorization          string
)

// Setup configures the roaming package.
func Setup(c config.Config) error {
	resolveNetIDDomainSuffix = c.Roaming.ResolveNetIDDomainSuffix
	netID = c.NetworkServer.NetID
	keks = make(map[string][]byte)
	agreements = []agreement{}

	defaultEnabled = c.Roaming.Default.Enabled
	defaultPassiveRoaming = c.Roaming.Default.PassiveRoaming
	defaultPassiveRoamingLifetime = c.Roaming.Default.PassiveRoamingLifetime
	defaultPassiveRoamingKEKLabel = c.Roaming.Default.PassiveRoamingKEKLabel
	defaultAsync = c.Roaming.Default.Async
	defaultAsyncTimeout = c.Roaming.Default.AsyncTimeout
	defaultServer = c.Roaming.Default.Server
	defaultCACert = c.Roaming.Default.CACert
	defaultTLSCert = c.Roaming.Default.TLSCert
	defaultTLSKey = c.Roaming.Default.TLSKey
	defaultAuthorization = c.Roaming.Default.Authorization

	if defaultEnabled {
		roamingEnabled = true
	}

	for _, server := range c.Roaming.Servers {
		roamingEnabled = true

		if server.Server == "" {
			server.Server = fmt.Sprintf("https://%s%s", server.NetID.String(), resolveNetIDDomainSuffix)
		}

		log.WithFields(log.Fields{
			"net_id":                   server.NetID,
			"passive_roaming":          server.PassiveRoaming,
			"passive_roaming_lifetime": server.PassiveRoamingLifetime,
			"server":                   server.Server,
			"async":                    server.Async,
			"async_timeout":            server.AsyncTimeout,
		}).Info("roaming: configuring roaming agreement")

		var redisClient redis.UniversalClient
		if server.Async {
			redisClient = storage.RedisClient()
		}

		client, err := backend.NewClient(backend.ClientConfig{
			Logger:        log.StandardLogger(),
			SenderID:      netID.String(),
			ReceiverID:    server.NetID.String(),
			Server:        server.Server,
			CACert:        server.CACert,
			TLSCert:       server.TLSCert,
			TLSKey:        server.TLSKey,
			Authorization: server.Authorization,
			AsyncTimeout:  server.AsyncTimeout,
			RedisClient:   redisClient,
		})
		if err != nil {
			return errors.Wrapf(err, "new roaming client error for netid: %s", server.NetID)
		}

		agreements = append(agreements, agreement{
			netID:                  server.NetID,
			passiveRoaming:         server.PassiveRoaming,
			passiveRoamingLifetime: server.PassiveRoamingLifetime,
			passiveRoamingKEKLabel: server.PassiveRoamingKEKLabel,
			client:                 client,
			server:                 server.Server,
		})
	}

	for _, k := range c.Roaming.KEK.Set {
		kek, err := hex.DecodeString(k.KEK)
		if err != nil {
			return errors.Wrap(err, "decode kek error")
		}

		keks[k.Label] = kek
	}

	return nil
}

// IsRoamingDevAddr returns true when the DevAddr does not match the NetID of
// the ChirpStack Network Server configuration. In case roaming is disabled,
// this will always return false.
// Note that enabling roaming -and- using ABP devices can be problematic when
// the ABP DevAddr does not match the NetID.
func IsRoamingDevAddr(devAddr lorawan.DevAddr) bool {
	return roamingEnabled && !devAddr.IsNetID(netID)
}

// IsRoamingEnabled returns if roaming is enabled.
func IsRoamingEnabled() bool {
	return roamingEnabled
}

// GetClientForNetID returns the API client for the given NetID.
func GetClientForNetID(clientNetID lorawan.NetID) (backend.Client, error) {
	for _, a := range agreements {
		if a.netID == clientNetID {
			return a.client, nil
		}
	}

	if defaultEnabled {
		var server string
		if defaultServer == "" {
			server = fmt.Sprintf("https://%s%s", clientNetID, resolveNetIDDomainSuffix)
		} else {
			server = defaultServer
		}

		log.WithFields(log.Fields{
			"net_id":                   clientNetID,
			"passive_roaming":          defaultPassiveRoaming,
			"passive_roaming_lifetime": defaultPassiveRoamingLifetime,
			"server":                   server,
			"async":                    defaultAsync,
			"async_timeout":            defaultAsyncTimeout,
		}).Info("roaming: configuring roaming agreement using default server")

		var redisClient redis.UniversalClient
		if defaultAsync {
			redisClient = storage.RedisClient()
		}

		client, err := backend.NewClient(backend.ClientConfig{
			Logger:       log.StandardLogger(),
			SenderID:     netID.String(),
			ReceiverID:   clientNetID.String(),
			Server:       server,
			CACert:       defaultCACert,
			TLSCert:      defaultTLSCert,
			TLSKey:       defaultTLSKey,
			AsyncTimeout: defaultAsyncTimeout,
			RedisClient:  redisClient,
		})
		if err != nil {
			return nil, errors.Wrapf(err, "new roaming client error for netid: %s", clientNetID)
		}
		return client, nil
	}

	return nil, ErrNoAgreement
}

// GetPassiveRoamingLifetime returns the passive-roaming lifetime for the
// given NetID.
func GetPassiveRoamingLifetime(netID lorawan.NetID) time.Duration {
	for _, a := range agreements {
		if a.netID == netID {
			return a.passiveRoamingLifetime
		}
	}

	if defaultEnabled {
		return defaultPassiveRoamingLifetime
	}

	return 0
}

// GetKEKKey returns the KEK key for the given label.
func GetKEKKey(label string) ([]byte, error) {
	kek, ok := keks[label]
	if !ok {
		return nil, fmt.Errorf("kek label '%s' is not configured", label)
	}
	return kek, nil
}

// GetPassiveRoamingKEKLabel returns the KEK label for the given NetID or an empty string.
func GetPassiveRoamingKEKLabel(netID lorawan.NetID) string {
	for _, a := range agreements {
		if a.netID == netID {
			return a.passiveRoamingKEKLabel
		}
	}

	if defaultEnabled {
		return defaultPassiveRoamingKEKLabel
	}

	return ""
}

// GetNetIDsForDevAddr returns the NetIDs matching the given DevAddr.
func GetNetIDsForDevAddr(devAddr lorawan.DevAddr) []lorawan.NetID {
	var out []lorawan.NetID

	for i := range agreements {
		a := agreements[i]
		if devAddr.IsNetID(a.netID) && a.passiveRoaming {
			out = append(out, a.netID)
		}
	}

	return out
}
<commit_msg>Configure roaming Authorization value from default client. (#573)<commit_after>package roaming

import (
	"encoding/hex"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/brocaar/chirpstack-network-server/v3/internal/config"
	"github.com/brocaar/chirpstack-network-server/v3/internal/storage"
	"github.com/brocaar/lorawan"
	"github.com/brocaar/lorawan/backend"
)

// ErrNoAgreement is returned when the requested agreement could not be found.
var ErrNoAgreement = errors.New("agreement not found")

type agreement struct {
	netID                  lorawan.NetID
	passiveRoaming         bool
	passiveRoamingLifetime time.Duration
	passiveRoamingKEKLabel string
	server                 string
	client                 backend.Client
}

var (
	resolveNetIDDomainSuffix string
	roamingEnabled           bool
	netID                    lorawan.NetID
	agreements               []agreement
	keks                     map[string][]byte

	defaultEnabled                bool
	defaultPassiveRoaming         bool
	defaultPassiveRoamingLifetime time.Duration
	defaultPassiveRoamingKEKLabel string
	defaultAsync                  bool
	defaultAsyncTimeout           time.Duration
	defaultServer                 string
	defaultCACert                 string
	defaultTLSCert                string
	defaultTLSKey                 string
	defaultAuthorization          string
)

// Setup configures the roaming package.
func Setup(c config.Config) error {
	resolveNetIDDomainSuffix = c.Roaming.ResolveNetIDDomainSuffix
	netID = c.NetworkServer.NetID
	keks = make(map[string][]byte)
	agreements = []agreement{}

	defaultEnabled = c.Roaming.Default.Enabled
	defaultPassiveRoaming = c.Roaming.Default.PassiveRoaming
	defaultPassiveRoamingLifetime = c.Roaming.Default.PassiveRoamingLifetime
	defaultPassiveRoamingKEKLabel = c.Roaming.Default.PassiveRoamingKEKLabel
	defaultAsync = c.Roaming.Default.Async
	defaultAsyncTimeout = c.Roaming.Default.AsyncTimeout
	defaultServer = c.Roaming.Default.Server
	defaultCACert = c.Roaming.Default.CACert
	defaultTLSCert = c.Roaming.Default.TLSCert
	defaultTLSKey = c.Roaming.Default.TLSKey
	defaultAuthorization = c.Roaming.Default.Authorization

	if defaultEnabled {
		roamingEnabled = true
	}

	for _, server := range c.Roaming.Servers {
		roamingEnabled = true

		if server.Server == "" {
			server.Server = fmt.Sprintf("https://%s%s", server.NetID.String(), resolveNetIDDomainSuffix)
		}

		log.WithFields(log.Fields{
			"net_id":                   server.NetID,
			"passive_roaming":          server.PassiveRoaming,
			"passive_roaming_lifetime": server.PassiveRoamingLifetime,
			"server":                   server.Server,
			"async":                    server.Async,
			"async_timeout":            server.AsyncTimeout,
		}).Info("roaming: configuring roaming agreement")

		var redisClient redis.UniversalClient
		if server.Async {
			redisClient = storage.RedisClient()
		}

		client, err := backend.NewClient(backend.ClientConfig{
			Logger:        log.StandardLogger(),
			SenderID:      netID.String(),
			ReceiverID:    server.NetID.String(),
			Server:        server.Server,
			CACert:        server.CACert,
			TLSCert:       server.TLSCert,
			TLSKey:        server.TLSKey,
			Authorization: server.Authorization,
			AsyncTimeout:  server.AsyncTimeout,
			RedisClient:   redisClient,
		})
		if err != nil {
			return errors.Wrapf(err, "new roaming client error for netid: %s", server.NetID)
		}

		agreements = append(agreements, agreement{
			netID:                  server.NetID,
			passiveRoaming:         server.PassiveRoaming,
			passiveRoamingLifetime: server.PassiveRoamingLifetime,
			passiveRoamingKEKLabel: server.PassiveRoamingKEKLabel,
			client:                 client,
			server:                 server.Server,
		})
	}

	for _, k := range c.Roaming.KEK.Set {
		kek, err := hex.DecodeString(k.KEK)
		if err != nil {
			return errors.Wrap(err, "decode kek error")
		}

		keks[k.Label] = kek
	}

	return nil
}

// IsRoamingDevAddr returns true when the DevAddr does not match the NetID of
// the ChirpStack Network Server configuration. In case roaming is disabled,
// this will always return false.
// Note that enabling roaming -and- using ABP devices can be problematic when
// the ABP DevAddr does not match the NetID.
func IsRoamingDevAddr(devAddr lorawan.DevAddr) bool {
	return roamingEnabled && !devAddr.IsNetID(netID)
}

// IsRoamingEnabled returns if roaming is enabled.
func IsRoamingEnabled() bool {
	return roamingEnabled
}

// GetClientForNetID returns the API client for the given NetID.
func GetClientForNetID(clientNetID lorawan.NetID) (backend.Client, error) {
	for _, a := range agreements {
		if a.netID == clientNetID {
			return a.client, nil
		}
	}

	if defaultEnabled {
		var server string
		if defaultServer == "" {
			server = fmt.Sprintf("https://%s%s", clientNetID, resolveNetIDDomainSuffix)
		} else {
			server = defaultServer
		}

		log.WithFields(log.Fields{
			"net_id":                   clientNetID,
			"passive_roaming":          defaultPassiveRoaming,
			"passive_roaming_lifetime": defaultPassiveRoamingLifetime,
			"server":                   server,
			"async":                    defaultAsync,
			"async_timeout":            defaultAsyncTimeout,
		}).Info("roaming: configuring roaming agreement using default server")

		var redisClient redis.UniversalClient
		if defaultAsync {
			redisClient = storage.RedisClient()
		}

		client, err := backend.NewClient(backend.ClientConfig{
			Logger:        log.StandardLogger(),
			SenderID:      netID.String(),
			ReceiverID:    clientNetID.String(),
			Server:        server,
			CACert:        defaultCACert,
			TLSCert:       defaultTLSCert,
			TLSKey:        defaultTLSKey,
			Authorization: defaultAuthorization,
			AsyncTimeout:  defaultAsyncTimeout,
			RedisClient:   redisClient,
		})
		if err != nil {
			return nil, errors.Wrapf(err, "new roaming client error for netid: %s", clientNetID)
		}
		return client, nil
	}

	return nil, ErrNoAgreement
}

// GetPassiveRoamingLifetime returns the passive-roaming lifetime for the
// given NetID.
func GetPassiveRoamingLifetime(netID lorawan.NetID) time.Duration {
	for _, a := range agreements {
		if a.netID == netID {
			return a.passiveRoamingLifetime
		}
	}

	if defaultEnabled {
		return defaultPassiveRoamingLifetime
	}

	return 0
}

// GetKEKKey returns the KEK key for the given label.
func GetKEKKey(label string) ([]byte, error) {
	kek, ok := keks[label]
	if !ok {
		return nil, fmt.Errorf("kek label '%s' is not configured", label)
	}
	return kek, nil
}

// GetPassiveRoamingKEKLabel returns the KEK label for the given NetID or an empty string.
func GetPassiveRoamingKEKLabel(netID lorawan.NetID) string {
	for _, a := range agreements {
		if a.netID == netID {
			return a.passiveRoamingKEKLabel
		}
	}

	if defaultEnabled {
		return defaultPassiveRoamingKEKLabel
	}

	return ""
}

// GetNetIDsForDevAddr returns the NetIDs matching the given DevAddr.
func GetNetIDsForDevAddr(devAddr lorawan.DevAddr) []lorawan.NetID {
	var out []lorawan.NetID

	for i := range agreements {
		a := agreements[i]
		if devAddr.IsNetID(a.netID) && a.passiveRoaming {
			out = append(out, a.netID)
		}
	}

	return out
}
<|endoftext|>


<commit_before>package summary

import (
	"strings"

	"github.com/go-task/task/v2/internal/logger"
	"github.com/go-task/task/v2/internal/taskfile"
)

func PrintAll(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
	for i, call := range c {
		printSpaceBetweenSummaries(i, l)
		Print(l, t.Tasks[call.Task])
	}
}

func printSpaceBetweenSummaries(i int, l *logger.Logger) {
	spaceRequired := i > 0
	if !spaceRequired {
		return
	}

	l.Outf("")
	l.Outf("")
}

func Print(l *logger.Logger, t *taskfile.Task) {
	printTaskName(l, t)
	if hasSummary(t) {
		printTaskSummary(l, t)
	} else if hasDescription(t) {
		printTaskDescription(l, t)
	} else {
		printNoDescriptionOrSummary(l)
	}
	printTaskDependencies(l, t)
	printTaskCommands(l, t)
}

func hasSummary(t *taskfile.Task) bool {
	return t.Summary != ""
}

func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
	lines := strings.Split(t.Summary, "\n")
	for i, line := range lines {
		notLastLine := i+1 < len(lines)
		if notLastLine || line != "" {
			l.Outf(line)
		}
	}
}

func printTaskName(l *logger.Logger, t *taskfile.Task) {
	l.Outf("task: %s", t.Task)
	l.Outf("")
}

func hasDescription(t *taskfile.Task) bool {
	return t.Desc != ""
}

func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
	l.Outf(t.Desc)
}

func printNoDescriptionOrSummary(l *logger.Logger) {
	l.Outf("(task does not have description or summary)")
}

func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
	hasDependencies := len(t.Deps) > 0
	if hasDependencies {
		l.Outf("")
		l.Outf("dependencies:")

		for _, d := range t.Deps {
			l.Outf(" - %s", d.Task)
		}
	}
}

func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
	hasCommands := len(t.Cmds) > 0
	if hasCommands {
		l.Outf("")
		l.Outf("commands:")
		for _, c := range t.Cmds {
			isCommand := c.Cmd != ""
			if isCommand {
				l.Outf(" - %s", c.Cmd)
			} else {
				l.Outf(" - Task: %s", c.Task)
			}
		}
	}
}
<commit_msg>refactoring<commit_after>package summary

import (
	"strings"

	"github.com/go-task/task/v2/internal/logger"
	"github.com/go-task/task/v2/internal/taskfile"
)

func PrintAll(l *logger.Logger, t *taskfile.Taskfile, c []taskfile.Call) {
	for i, call := range c {
		printSpaceBetweenSummaries(i, l)
		Print(l, t.Tasks[call.Task])
	}
}

func printSpaceBetweenSummaries(i int, l *logger.Logger) {
	spaceRequired := i > 0
	if !spaceRequired {
		return
	}

	l.Outf("")
	l.Outf("")
}

func Print(l *logger.Logger, t *taskfile.Task) {
	printTaskName(l, t)
	if hasSummary(t) {
		printTaskSummary(l, t)
	} else if hasDescription(t) {
		printTaskDescription(l, t)
	} else {
		printNoDescriptionOrSummary(l)
	}
	printTaskDependencies(l, t)
	printTaskCommands(l, t)
}

func hasSummary(t *taskfile.Task) bool {
	return t.Summary != ""
}

func printTaskSummary(l *logger.Logger, t *taskfile.Task) {
	lines := strings.Split(t.Summary, "\n")
	for i, line := range lines {
		notLastLine := i+1 < len(lines)
		if notLastLine || line != "" {
			l.Outf(line)
		}
	}
}

func printTaskName(l *logger.Logger, t *taskfile.Task) {
	l.Outf("task: %s", t.Task)
	l.Outf("")
}

func hasDescription(t *taskfile.Task) bool {
	return t.Desc != ""
}

func printTaskDescription(l *logger.Logger, t *taskfile.Task) {
	l.Outf(t.Desc)
}

func printNoDescriptionOrSummary(l *logger.Logger) {
	l.Outf("(task does not have description or summary)")
}

func printTaskDependencies(l *logger.Logger, t *taskfile.Task) {
	hasDependencies := len(t.Deps) > 0
	if hasDependencies {
		l.Outf("")
		l.Outf("dependencies:")

		for _, d := range t.Deps {
			l.Outf(" - %s", d.Task)
		}
	}
}

func printTaskCommands(l *logger.Logger, t *taskfile.Task) {
	noCommands := len(t.Cmds) == 0
	if noCommands {
		return
	}

	l.Outf("")
	l.Outf("commands:")
	for _, c := range t.Cmds {
		isCommand := c.Cmd != ""
		if isCommand {
			l.Outf(" - %s", c.Cmd)
		} else {
			l.Outf(" - Task: %s", c.Task)
		}
	}
}
<|endoftext|>


<commit_before>package network // import "collectd.org/network"

import (
	"context"
	"log"
	"net"
	"os"

	"collectd.org/format"
)

// This example demonstrates how to listen to encrypted network traffic and
// dump it to STDOUT using format.Putval.
func ExampleServer_ListenAndWrite() {
	srv := &Server{
		Addr:           net.JoinHostPort("::", DefaultService),
		Writer:         format.NewPutval(os.Stdout),
		PasswordLookup: NewAuthFile("/etc/collectd/users"),
	}

	// blocks
	log.Fatal(srv.ListenAndWrite(context.Background()))
}

// This example demonstrates how to forward received IPv6 multicast traffic to
// a unicast address, using PSK encryption.
func ExampleListenAndWrite() {
	opts := ClientOptions{
		SecurityLevel: Encrypt,
		Username:      "collectd",
		Password:      "dieXah7e",
	}
	client, err := Dial(net.JoinHostPort("example.com", DefaultService), opts)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// blocks
	log.Fatal(ListenAndWrite(context.Background(), ":"+DefaultService, client))
}
<commit_msg>Package network: Add server cancellation test.<commit_after>package network // import "collectd.org/network"

import (
	"context"
	"log"
	"net"
	"os"
	"sync"
	"testing"
	"time"

	"collectd.org/format"
)

// This example demonstrates how to listen to encrypted network traffic and
// dump it to STDOUT using format.Putval.
func ExampleServer_ListenAndWrite() {
	srv := &Server{
		Addr:           net.JoinHostPort("::", DefaultService),
		Writer:         format.NewPutval(os.Stdout),
		PasswordLookup: NewAuthFile("/etc/collectd/users"),
	}

	// blocks
	log.Fatal(srv.ListenAndWrite(context.Background()))
}

// This example demonstrates how to forward received IPv6 multicast traffic to
// a unicast address, using PSK encryption.
func ExampleListenAndWrite() {
	opts := ClientOptions{
		SecurityLevel: Encrypt,
		Username:      "collectd",
		Password:      "dieXah7e",
	}
	client, err := Dial(net.JoinHostPort("example.com", DefaultService), opts)
	if err != nil {
		log.Fatal(err)
	}
	defer client.Close()

	// blocks
	log.Fatal(ListenAndWrite(context.Background(), ":"+DefaultService, client))
}

func TestServer_Cancellation(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())

	wg := &sync.WaitGroup{}
	wg.Add(1)

	var srvErr error
	go func() {
		srv := &Server{
			Addr: "localhost:" + DefaultService,
		}

		srvErr = srv.ListenAndWrite(ctx)
		wg.Done()
	}()

	// wait for a bit, then shut down the server
	time.Sleep(100 * time.Millisecond)
	cancel()
	wg.Wait()

	if srvErr != context.Canceled {
		t.Errorf("srvErr = %#v, want %#v", srvErr, context.Canceled)
	}
}
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package cli

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"mynewt.apache.org/newt/newt/builder"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/newt/target"
	"mynewt.apache.org/newt/util"
)

const TARGET_TEST_NAME = "unittest"

var testablePkgMap map[*pkg.LocalPackage]struct{}

func testablePkgs() map[*pkg.LocalPackage]struct{} {
	if testablePkgMap != nil {
		return testablePkgMap
	}

	testablePkgMap := map[*pkg.LocalPackage]struct{}{}

	// Create a map of path => lclPkg.
	proj, err := project.TryGetProject()
	if err != nil {
		return nil
	}

	allPkgs := proj.PackagesOfType(-1)
	pathLpkgMap := make(map[string]*pkg.LocalPackage, len(allPkgs))
	for _, p := range allPkgs {
		lpkg := p.(*pkg.LocalPackage)
		pathLpkgMap[lpkg.BasePath()] = lpkg
	}

	// Add all unit test packages to the testable package map.
	testPkgs := proj.PackagesOfType(pkg.PACKAGE_TYPE_UNITTEST)
	for _, p := range testPkgs {
		lclPack := p.(*pkg.LocalPackage)
		testablePkgMap[lclPack] = struct{}{}
	}

	// Next add first ancestor of each test package.
	for testPkg, _ := range testablePkgMap {
		for cur := filepath.Dir(testPkg.BasePath()); cur != proj.BasePath; cur = filepath.Dir(cur) {
			lpkg := pathLpkgMap[cur]
			if lpkg != nil && lpkg.Type() != pkg.PACKAGE_TYPE_UNITTEST {
				testablePkgMap[lpkg] = struct{}{}
				break
			}
		}
	}

	return testablePkgMap
}

func pkgToUnitTests(pack *pkg.LocalPackage) []*pkg.LocalPackage {
	// If the user specified a unittest package, just test that one.
	if pack.Type() == pkg.PACKAGE_TYPE_UNITTEST {
		return []*pkg.LocalPackage{pack}
	}

	// Otherwise, return all the package's direct descendants that are unit
	// test packages.
	result := []*pkg.LocalPackage{}
	srcPath := pack.BasePath()
	for p, _ := range testablePkgs() {
		if p.Type() == pkg.PACKAGE_TYPE_UNITTEST &&
			filepath.Dir(p.BasePath()) == srcPath {

			result = append(result, p)
		}
	}

	return result
}

var extraJtagCmd string

func buildRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, nil)
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	// Verify that all target names are valid.
	_, err := ResolveTargets(args...)
	if err != nil {
		NewtUsage(cmd, err)
	}

	for _, targetName := range args {
		// Reset the global state for the next build.
		if err := ResetGlobalState(); err != nil {
			NewtUsage(nil, err)
		}

		// Lookup the target by name.  This has to be done a second time here
		// now that the project has been reset.
		t := ResolveTarget(targetName)
		if t == nil {
			NewtUsage(nil, util.NewNewtError("Failed to resolve target: "+
				targetName))
		}

		util.StatusMessage(util.VERBOSITY_DEFAULT, "Building target %s\n",
			t.FullName())

		b, err := builder.NewTargetBuilder(t, nil)
		if err != nil {
			NewtUsage(nil, err)
		}

		if err := b.Build(); err != nil {
			NewtUsage(nil, err)
		}

		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"Target successfully built: %s\n", targetName)

		/* TODO */
	}
}

func cleanRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	cleanAll := false
	targets := []*target.Target{}
	for _, arg := range args {
		if arg == TARGET_KEYWORD_ALL {
			cleanAll = true
		} else {
			t := ResolveTarget(arg)
			if t == nil {
				NewtUsage(cmd, util.NewNewtError("invalid target name: "+arg))
			}
			targets = append(targets, t)
		}
	}

	if cleanAll {
		path := builder.BinRoot()
		util.StatusMessage(util.VERBOSITY_VERBOSE,
			"Cleaning directory %s\n", path)

		err := os.RemoveAll(path)
		if err != nil {
			NewtUsage(cmd, err)
		}
	} else {
		for _, t := range targets {
			b, err := builder.NewTargetBuilder(t, nil)
			if err != nil {
				NewtUsage(nil, err)
			}
			if err := b.Clean(); err != nil {
				NewtUsage(cmd, err)
			}
		}
	}
}

func pkgnames(pkgs []*pkg.LocalPackage) string {
	s := ""

	for _, p := range pkgs {
		s += p.Name() + " "
	}

	return s
}

func testRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, nil)
	}

	proj, err := project.TryGetProject()
	if err != nil {
		NewtUsage(nil, err)
	}

	// Verify and resolve each specified package.
	testAll := false
	packs := []*pkg.LocalPackage{}
	for _, pkgName := range args {
		if pkgName == "all" {
			testAll = true
		} else {
			pack, err := proj.ResolvePackage(proj.LocalRepo(), pkgName)
			if err != nil {
				NewtUsage(cmd, err)
			}

			testPkgs := pkgToUnitTests(pack)
			if len(testPkgs) == 0 {
				NewtUsage(nil, util.FmtNewtError("Package %s contains no "+
					"unit tests", pack.FullName()))
			}

			packs = append(packs, testPkgs...)
		}
	}

	if testAll {
		packItfs := proj.PackagesOfType(pkg.PACKAGE_TYPE_UNITTEST)
		packs = make([]*pkg.LocalPackage, len(packItfs))
		for i, p := range packItfs {
			packs[i] = p.(*pkg.LocalPackage)
		}

		packs = pkg.SortLclPkgs(packs)
	}

	if len(packs) == 0 {
		NewtUsage(nil, util.NewNewtError("No testable packages found"))
	}

	passedPkgs := []*pkg.LocalPackage{}
	failedPkgs := []*pkg.LocalPackage{}
	for _, pack := range packs {
		// Reset the global state for the next test.
		if err := ResetGlobalState(); err != nil {
			NewtUsage(nil, err)
		}

		// Each unit test package gets its own target.  This target is a copy
		// of the base unit test package, just with an appropriate name.  The
		// reason each test needs a unique target is: syscfg and sysinit are
		// target-specific.  If each test package shares a target, they will
		// overwrite these generated headers each time they are run.  Worse, if
		// two tests are run back-to-back, the timestamps may indicate that the
		// headers have not changed between tests, causing build failures.
		baseTarget := ResolveTarget(TARGET_TEST_NAME)
		if baseTarget == nil {
			NewtUsage(nil, util.NewNewtError("Can't find unit test target: "+
				TARGET_TEST_NAME))
		}

		targetName := fmt.Sprintf("%s/%s/%s",
			TARGET_DEFAULT_DIR, TARGET_TEST_NAME,
			builder.TestTargetName(pack.Name()))

		t := ResolveTarget(targetName)
		if t == nil {
			targetName, err := ResolveNewTargetName(targetName)
			if err != nil {
				NewtUsage(nil, err)
			}

			t = baseTarget.Clone(proj.LocalRepo(), targetName)
			if err := t.Save(); err != nil {
				NewtUsage(nil, err)
			}
		}

		b, err := builder.NewTargetBuilder(t, pack)
		if err != nil {
			NewtUsage(nil, err)
		}

		util.StatusMessage(util.VERBOSITY_DEFAULT, "Testing package %s\n",
			pack.FullName())

		err = b.Test()
		if err == nil {
			passedPkgs = append(passedPkgs, pack)
		} else {
			newtError := err.(*util.NewtError)
			util.StatusMessage(util.VERBOSITY_QUIET, newtError.Text)
			failedPkgs = append(failedPkgs, pack)
		}
	}

	passStr := fmt.Sprintf("Passed tests: [%s]", PackageNameList(passedPkgs))
	failStr := fmt.Sprintf("Failed tests: [%s]", PackageNameList(failedPkgs))

	if len(failedPkgs) > 0 {
		NewtUsage(nil, util.FmtNewtError("Test failure(s):\n%s\n%s", passStr,
			failStr))
	} else {
		util.StatusMessage(util.VERBOSITY_DEFAULT, "%s\n", passStr)
		util.StatusMessage(util.VERBOSITY_DEFAULT, "All tests passed\n")
	}
}

func loadRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	t := ResolveTarget(args[0])
	if t == nil {
		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
	}

	b, err := builder.NewTargetBuilder(t, nil)
	if err != nil {
		NewtUsage(nil, err)
	}

	if err := b.Load(extraJtagCmd); err != nil {
		NewtUsage(cmd, err)
	}
}

func debugRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	t := ResolveTarget(args[0])
	if t == nil {
		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
	}

	b, err := builder.NewTargetBuilder(t, nil)
	if err != nil {
		NewtUsage(nil, err)
	}

	if err := b.Debug(extraJtagCmd, false); err != nil {
		NewtUsage(cmd, err)
	}
}

func sizeRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	t := ResolveTarget(args[0])
	if t == nil {
		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
	}

	b, err := builder.NewTargetBuilder(t, nil)
	if err != nil {
		NewtUsage(nil, err)
	}

	if err := b.Size(); err != nil {
		NewtUsage(cmd, err)
	}
}

func AddBuildCommands(cmd *cobra.Command) {
	buildCmd := &cobra.Command{
		Use:   "build <target-name> [target-names...]",
		Short: "Builds one or more targets.",
		Run:   buildRunCmd,
	}

	buildCmd.ValidArgs = targetList()
	cmd.AddCommand(buildCmd)

	cleanCmd := &cobra.Command{
		Use:   "clean <target-name> [target-names...] | all",
		Short: "Deletes build artifacts for one or more targets.",
		Run:   cleanRunCmd,
	}

	cleanCmd.ValidArgs = append(targetList(), "all")
	cmd.AddCommand(cleanCmd)

	testCmd := &cobra.Command{
		Use:   "test <package-name> [package-names...] | all",
		Short: "Executes unit tests for one or more packages",
		Run:   testRunCmd,
	}
	testCmd.ValidArgs = append(packageList(), "all")
	cmd.AddCommand(testCmd)

	loadHelpText := "Load app image to target for <target-name>."

	loadCmd := &cobra.Command{
		Use:   "load <target-name>",
		Short: "Load built target to board",
		Long:  loadHelpText,
		Run:   loadRunCmd,
	}

	loadCmd.ValidArgs = targetList()
	cmd.AddCommand(loadCmd)
	loadCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "j", "",
		"extra commands to send to JTAG software")

	debugHelpText := "Open debugger session for <target-name>."

	debugCmd := &cobra.Command{
		Use:   "debug <target-name>",
		Short: "Open debugger session to target",
		Long:  debugHelpText,
		Run:   debugRunCmd,
	}

	debugCmd.ValidArgs = targetList()
	cmd.AddCommand(debugCmd)
	debugCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "j", "",
		"extra commands to send to JTAG software")

	sizeHelpText := "Calculate the size of target components specified by " +
		"<target-name>."

	sizeCmd := &cobra.Command{
		Use:   "size <target-name>",
		Short: "Size of target components",
		Long:  sizeHelpText,
		Run:   sizeRunCmd,
	}

	sizeCmd.ValidArgs = targetList()
	cmd.AddCommand(sizeCmd)

}
<commit_msg>newt - Fix "clean unittest"<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package cli

import (
	"fmt"
	"os"
	"path/filepath"

	"github.com/spf13/cobra"
	"mynewt.apache.org/newt/newt/builder"
	"mynewt.apache.org/newt/newt/pkg"
	"mynewt.apache.org/newt/newt/project"
	"mynewt.apache.org/newt/newt/target"
	"mynewt.apache.org/newt/util"
)

const TARGET_TEST_NAME = "unittest"

var testablePkgMap map[*pkg.LocalPackage]struct{}

func testablePkgs() map[*pkg.LocalPackage]struct{} {
	if testablePkgMap != nil {
		return testablePkgMap
	}

	testablePkgMap := map[*pkg.LocalPackage]struct{}{}

	// Create a map of path => lclPkg.
	proj, err := project.TryGetProject()
	if err != nil {
		return nil
	}

	allPkgs := proj.PackagesOfType(-1)
	pathLpkgMap := make(map[string]*pkg.LocalPackage, len(allPkgs))
	for _, p := range allPkgs {
		lpkg := p.(*pkg.LocalPackage)
		pathLpkgMap[lpkg.BasePath()] = lpkg
	}

	// Add all unit test packages to the testable package map.
	testPkgs := proj.PackagesOfType(pkg.PACKAGE_TYPE_UNITTEST)
	for _, p := range testPkgs {
		lclPack := p.(*pkg.LocalPackage)
		testablePkgMap[lclPack] = struct{}{}
	}

	// Next add first ancestor of each test package.
	for testPkg, _ := range testablePkgMap {
		for cur := filepath.Dir(testPkg.BasePath()); cur != proj.BasePath; cur = filepath.Dir(cur) {
			lpkg := pathLpkgMap[cur]
			if lpkg != nil && lpkg.Type() != pkg.PACKAGE_TYPE_UNITTEST {
				testablePkgMap[lpkg] = struct{}{}
				break
			}
		}
	}

	return testablePkgMap
}

func pkgToUnitTests(pack *pkg.LocalPackage) []*pkg.LocalPackage {
	// If the user specified a unittest package, just test that one.
	if pack.Type() == pkg.PACKAGE_TYPE_UNITTEST {
		return []*pkg.LocalPackage{pack}
	}

	// Otherwise, return all the package's direct descendants that are unit
	// test packages.
	result := []*pkg.LocalPackage{}
	srcPath := pack.BasePath()
	for p, _ := range testablePkgs() {
		if p.Type() == pkg.PACKAGE_TYPE_UNITTEST &&
			filepath.Dir(p.BasePath()) == srcPath {

			result = append(result, p)
		}
	}

	return result
}

var extraJtagCmd string

func buildRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, nil)
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	// Verify that all target names are valid.
	_, err := ResolveTargets(args...)
	if err != nil {
		NewtUsage(cmd, err)
	}

	for _, targetName := range args {
		// Reset the global state for the next build.
		if err := ResetGlobalState(); err != nil {
			NewtUsage(nil, err)
		}

		// Lookup the target by name.  This has to be done a second time here
		// now that the project has been reset.
		t := ResolveTarget(targetName)
		if t == nil {
			NewtUsage(nil, util.NewNewtError("Failed to resolve target: "+
				targetName))
		}

		util.StatusMessage(util.VERBOSITY_DEFAULT, "Building target %s\n",
			t.FullName())

		b, err := builder.NewTargetBuilder(t, nil)
		if err != nil {
			NewtUsage(nil, err)
		}

		if err := b.Build(); err != nil {
			NewtUsage(nil, err)
		}

		util.StatusMessage(util.VERBOSITY_DEFAULT,
			"Target successfully built: %s\n", targetName)

		/* TODO */
	}
}

func cleanDir(path string) {
	util.StatusMessage(util.VERBOSITY_VERBOSE,
		"Cleaning directory %s\n", path)

	err := os.RemoveAll(path)
	if err != nil {
		NewtUsage(nil, util.NewNewtError(err.Error()))
	}
}

func cleanRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	cleanAll := false
	targets := []*target.Target{}
	for _, arg := range args {
		if arg == TARGET_KEYWORD_ALL {
			cleanAll = true
		} else {
			t := ResolveTarget(arg)
			if t == nil {
				NewtUsage(cmd, util.NewNewtError("invalid target name: "+arg))
			}
			targets = append(targets, t)
		}
	}

	if cleanAll {
		cleanDir(builder.BinRoot())
	} else {
		for _, t := range targets {
			cleanDir(builder.TargetBinDir(t))
		}
	}
}

func pkgnames(pkgs []*pkg.LocalPackage) string {
	s := ""

	for _, p := range pkgs {
		s += p.Name() + " "
	}

	return s
}

func testRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, nil)
	}

	proj, err := project.TryGetProject()
	if err != nil {
		NewtUsage(nil, err)
	}

	// Verify and resolve each specified package.
	testAll := false
	packs := []*pkg.LocalPackage{}
	for _, pkgName := range args {
		if pkgName == "all" {
			testAll = true
		} else {
			pack, err := proj.ResolvePackage(proj.LocalRepo(), pkgName)
			if err != nil {
				NewtUsage(cmd, err)
			}

			testPkgs := pkgToUnitTests(pack)
			if len(testPkgs) == 0 {
				NewtUsage(nil, util.FmtNewtError("Package %s contains no "+
					"unit tests", pack.FullName()))
			}

			packs = append(packs, testPkgs...)
		}
	}

	if testAll {
		packItfs := proj.PackagesOfType(pkg.PACKAGE_TYPE_UNITTEST)
		packs = make([]*pkg.LocalPackage, len(packItfs))
		for i, p := range packItfs {
			packs[i] = p.(*pkg.LocalPackage)
		}

		packs = pkg.SortLclPkgs(packs)
	}

	if len(packs) == 0 {
		NewtUsage(nil, util.NewNewtError("No testable packages found"))
	}

	passedPkgs := []*pkg.LocalPackage{}
	failedPkgs := []*pkg.LocalPackage{}
	for _, pack := range packs {
		// Reset the global state for the next test.
		if err := ResetGlobalState(); err != nil {
			NewtUsage(nil, err)
		}

		// Each unit test package gets its own target.  This target is a copy
		// of the base unit test package, just with an appropriate name.  The
		// reason each test needs a unique target is: syscfg and sysinit are
		// target-specific.  If each test package shares a target, they will
		// overwrite these generated headers each time they are run.  Worse, if
		// two tests are run back-to-back, the timestamps may indicate that the
		// headers have not changed between tests, causing build failures.
		baseTarget := ResolveTarget(TARGET_TEST_NAME)
		if baseTarget == nil {
			NewtUsage(nil, util.NewNewtError("Can't find unit test target: "+
				TARGET_TEST_NAME))
		}

		targetName := fmt.Sprintf("%s/%s/%s",
			TARGET_DEFAULT_DIR, TARGET_TEST_NAME,
			builder.TestTargetName(pack.Name()))

		t := ResolveTarget(targetName)
		if t == nil {
			targetName, err := ResolveNewTargetName(targetName)
			if err != nil {
				NewtUsage(nil, err)
			}

			t = baseTarget.Clone(proj.LocalRepo(), targetName)
			if err := t.Save(); err != nil {
				NewtUsage(nil, err)
			}
		}

		b, err := builder.NewTargetBuilder(t, pack)
		if err != nil {
			NewtUsage(nil, err)
		}

		util.StatusMessage(util.VERBOSITY_DEFAULT, "Testing package %s\n",
			pack.FullName())

		err = b.Test()
		if err == nil {
			passedPkgs = append(passedPkgs, pack)
		} else {
			newtError := err.(*util.NewtError)
			util.StatusMessage(util.VERBOSITY_QUIET, newtError.Text)
			failedPkgs = append(failedPkgs, pack)
		}
	}

	passStr := fmt.Sprintf("Passed tests: [%s]", PackageNameList(passedPkgs))
	failStr := fmt.Sprintf("Failed tests: [%s]", PackageNameList(failedPkgs))

	if len(failedPkgs) > 0 {
		NewtUsage(nil, util.FmtNewtError("Test failure(s):\n%s\n%s", passStr,
			failStr))
	} else {
		util.StatusMessage(util.VERBOSITY_DEFAULT, "%s\n", passStr)
		util.StatusMessage(util.VERBOSITY_DEFAULT, "All tests passed\n")
	}
}

func loadRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	t := ResolveTarget(args[0])
	if t == nil {
		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
	}

	b, err := builder.NewTargetBuilder(t, nil)
	if err != nil {
		NewtUsage(nil, err)
	}

	if err := b.Load(extraJtagCmd); err != nil {
		NewtUsage(cmd, err)
	}
}

func debugRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	t := ResolveTarget(args[0])
	if t == nil {
		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
	}

	b, err := builder.NewTargetBuilder(t, nil)
	if err != nil {
		NewtUsage(nil, err)
	}

	if err := b.Debug(extraJtagCmd, false); err != nil {
		NewtUsage(cmd, err)
	}
}

func sizeRunCmd(cmd *cobra.Command, args []string) {
	if len(args) < 1 {
		NewtUsage(cmd, util.NewNewtError("Must specify target"))
	}

	if _, err := project.TryGetProject(); err != nil {
		NewtUsage(nil, err)
	}

	t := ResolveTarget(args[0])
	if t == nil {
		NewtUsage(cmd, util.NewNewtError("Invalid target name: "+args[0]))
	}

	b, err := builder.NewTargetBuilder(t, nil)
	if err != nil {
		NewtUsage(nil, err)
	}

	if err := b.Size(); err != nil {
		NewtUsage(cmd, err)
	}
}

func AddBuildCommands(cmd *cobra.Command) {
	buildCmd := &cobra.Command{
		Use:   "build <target-name> [target-names...]",
		Short: "Builds one or more targets.",
		Run:   buildRunCmd,
	}

	buildCmd.ValidArgs = targetList()
	cmd.AddCommand(buildCmd)

	cleanCmd := &cobra.Command{
		Use:   "clean <target-name> [target-names...] | all",
		Short: "Deletes build artifacts for one or more targets.",
		Run:   cleanRunCmd,
	}

	cleanCmd.ValidArgs = append(targetList(), "all")
	cmd.AddCommand(cleanCmd)

	testCmd := &cobra.Command{
		Use:   "test <package-name> [package-names...] | all",
		Short: "Executes unit tests for one or more packages",
		Run:   testRunCmd,
	}
	testCmd.ValidArgs = append(packageList(), "all")
	cmd.AddCommand(testCmd)

	loadHelpText := "Load app image to target for <target-name>."

	loadCmd := &cobra.Command{
		Use:   "load <target-name>",
		Short: "Load built target to board",
		Long:  loadHelpText,
		Run:   loadRunCmd,
	}

	loadCmd.ValidArgs = targetList()
	cmd.AddCommand(loadCmd)
	loadCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "j", "",
		"extra commands to send to JTAG software")

	debugHelpText := "Open debugger session for <target-name>."

	debugCmd := &cobra.Command{
		Use:   "debug <target-name>",
		Short: "Open debugger session to target",
		Long:  debugHelpText,
		Run:   debugRunCmd,
	}

	debugCmd.ValidArgs = targetList()
	cmd.AddCommand(debugCmd)
	debugCmd.PersistentFlags().StringVarP(&extraJtagCmd, "extrajtagcmd", "j", "",
		"extra commands to send to JTAG software")

	sizeHelpText := "Calculate the size of target components specified by " +
		"<target-name>."

	sizeCmd := &cobra.Command{
		Use:   "size <target-name>",
		Short: "Size of target components",
		Long:  sizeHelpText,
		Run:   sizeRunCmd,
	}

	sizeCmd.ValidArgs = targetList()
	cmd.AddCommand(sizeCmd)

}
<|endoftext|>


<commit_before>package schema

import (
	"reflect"
	"testing"
)

func TestMapFieldWriter_impl(t *testing.T) {
	var _ FieldWriter = new(MapFieldWriter)
}

func TestMapFieldWriter(t *testing.T) {
	schema := map[string]*Schema{
		"bool":   &Schema{Type: TypeBool},
		"int":    &Schema{Type: TypeInt},
		"string": &Schema{Type: TypeString},
		"list": &Schema{
			Type: TypeList,
			Elem: &Schema{Type: TypeString},
		},
		"listInt": &Schema{
			Type: TypeList,
			Elem: &Schema{Type: TypeInt},
		},
		"listResource": &Schema{
			Type:     TypeList,
			Optional: true,
			Computed: true,
			Elem: &Resource{
				Schema: map[string]*Schema{
					"value": &Schema{
						Type:     TypeInt,
						Optional: true,
					},
				},
			},
		},
		"map": &Schema{Type: TypeMap},
		"set": &Schema{
			Type: TypeSet,
			Elem: &Schema{Type: TypeInt},
			Set: func(a interface{}) int {
				return a.(int)
			},
		},
		"setDeep": &Schema{
			Type: TypeSet,
			Elem: &Resource{
				Schema: map[string]*Schema{
					"index": &Schema{Type: TypeInt},
					"value": &Schema{Type: TypeString},
				},
			},
			Set: func(a interface{}) int {
				return a.(map[string]interface{})["index"].(int)
			},
		},
	}

	cases := map[string]struct {
		Addr  []string
		Value interface{}
		Err   bool
		Out   map[string]string
	}{
		"noexist": {
			[]string{"noexist"},
			42,
			true,
			map[string]string{},
		},

		"bool": {
			[]string{"bool"},
			false,
			false,
			map[string]string{
				"bool": "false",
			},
		},

		"int": {
			[]string{"int"},
			42,
			false,
			map[string]string{
				"int": "42",
			},
		},

		"string": {
			[]string{"string"},
			"42",
			false,
			map[string]string{
				"string": "42",
			},
		},

		"list of resources": {
			[]string{"listResource"},
			[]interface{}{
				map[string]interface{}{
					"value": 80,
				},
			},
			false,
			map[string]string{
				"listResource.#":       "1",
				"listResource.0.value": "80",
			},
		},

		"list of resources empty": {
			[]string{"listResource"},
			[]interface{}{},
			false,
			map[string]string{
				"listResource.#": "0",
			},
		},

		"list of strings": {
			[]string{"list"},
			[]interface{}{"foo", "bar"},
			false,
			map[string]string{
				"list.#": "2",
				"list.0": "foo",
				"list.1": "bar",
			},
		},

		"list element": {
			[]string{"list", "0"},
			"string",
			true,
			map[string]string{},
		},

		"map": {
			[]string{"map"},
			map[string]interface{}{"foo": "bar"},
			false,
			map[string]string{
				"map.#":   "1",
				"map.foo": "bar",
			},
		},

		"map delete": {
			[]string{"map"},
			nil,
			false,
			map[string]string{
				"map": "",
			},
		},

		"map element": {
			[]string{"map", "foo"},
			"bar",
			true,
			map[string]string{},
		},

		"set": {
			[]string{"set"},
			[]interface{}{1, 2, 5},
			false,
			map[string]string{
				"set.#": "3",
				"set.1": "1",
				"set.2": "2",
				"set.5": "5",
			},
		},

		"set nil": {
			[]string{"set"},
			nil,
			false,
			map[string]string{
				"set.#": "0",
			},
		},

		"set resource": {
			[]string{"setDeep"},
			[]interface{}{
				map[string]interface{}{
					"index": 10,
					"value": "foo",
				},
				map[string]interface{}{
					"index": 50,
					"value": "bar",
				},
			},
			false,
			map[string]string{
				"setDeep.#":        "2",
				"setDeep.10.index": "10",
				"setDeep.10.value": "foo",
				"setDeep.50.index": "50",
				"setDeep.50.value": "bar",
			},
		},

		"set element": {
			[]string{"set", "5"},
			5,
			true,
			map[string]string{},
		},

		"full object": {
			nil,
			map[string]interface{}{
				"string": "foo",
				"list":   []interface{}{"foo", "bar"},
			},
			false,
			map[string]string{
				"string": "foo",
				"list.#": "2",
				"list.0": "foo",
				"list.1": "bar",
			},
		},
	}

	for name, tc := range cases {
		w := &MapFieldWriter{Schema: schema}
		err := w.WriteField(tc.Addr, tc.Value)
		if (err != nil) != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}

		actual := w.Map()
		if !reflect.DeepEqual(actual, tc.Out) {
			t.Fatalf("%s: bad: %#v", name, actual)
		}
	}
}
<commit_msg>helper/schema: test that set can be nil<commit_after>package schema

import (
	"reflect"
	"testing"
)

func TestMapFieldWriter_impl(t *testing.T) {
	var _ FieldWriter = new(MapFieldWriter)
}

func TestMapFieldWriter(t *testing.T) {
	schema := map[string]*Schema{
		"bool":   &Schema{Type: TypeBool},
		"int":    &Schema{Type: TypeInt},
		"string": &Schema{Type: TypeString},
		"list": &Schema{
			Type: TypeList,
			Elem: &Schema{Type: TypeString},
		},
		"listInt": &Schema{
			Type: TypeList,
			Elem: &Schema{Type: TypeInt},
		},
		"listResource": &Schema{
			Type:     TypeList,
			Optional: true,
			Computed: true,
			Elem: &Resource{
				Schema: map[string]*Schema{
					"value": &Schema{
						Type:     TypeInt,
						Optional: true,
					},
				},
			},
		},
		"map": &Schema{Type: TypeMap},
		"set": &Schema{
			Type: TypeSet,
			Elem: &Schema{Type: TypeInt},
			Set: func(a interface{}) int {
				return a.(int)
			},
		},
		"setDeep": &Schema{
			Type: TypeSet,
			Elem: &Resource{
				Schema: map[string]*Schema{
					"index": &Schema{Type: TypeInt},
					"value": &Schema{Type: TypeString},
				},
			},
			Set: func(a interface{}) int {
				return a.(map[string]interface{})["index"].(int)
			},
		},
	}

	cases := map[string]struct {
		Addr  []string
		Value interface{}
		Err   bool
		Out   map[string]string
	}{
		"noexist": {
			[]string{"noexist"},
			42,
			true,
			map[string]string{},
		},

		"bool": {
			[]string{"bool"},
			false,
			false,
			map[string]string{
				"bool": "false",
			},
		},

		"int": {
			[]string{"int"},
			42,
			false,
			map[string]string{
				"int": "42",
			},
		},

		"string": {
			[]string{"string"},
			"42",
			false,
			map[string]string{
				"string": "42",
			},
		},

		"list of resources": {
			[]string{"listResource"},
			[]interface{}{
				map[string]interface{}{
					"value": 80,
				},
			},
			false,
			map[string]string{
				"listResource.#":       "1",
				"listResource.0.value": "80",
			},
		},

		"list of resources empty": {
			[]string{"listResource"},
			[]interface{}{},
			false,
			map[string]string{
				"listResource.#": "0",
			},
		},

		"list of resources nil": {
			[]string{"listResource"},
			nil,
			false,
			map[string]string{
				"listResource.#": "0",
			},
		},

		"list of strings": {
			[]string{"list"},
			[]interface{}{"foo", "bar"},
			false,
			map[string]string{
				"list.#": "2",
				"list.0": "foo",
				"list.1": "bar",
			},
		},

		"list element": {
			[]string{"list", "0"},
			"string",
			true,
			map[string]string{},
		},

		"map": {
			[]string{"map"},
			map[string]interface{}{"foo": "bar"},
			false,
			map[string]string{
				"map.#":   "1",
				"map.foo": "bar",
			},
		},

		"map delete": {
			[]string{"map"},
			nil,
			false,
			map[string]string{
				"map": "",
			},
		},

		"map element": {
			[]string{"map", "foo"},
			"bar",
			true,
			map[string]string{},
		},

		"set": {
			[]string{"set"},
			[]interface{}{1, 2, 5},
			false,
			map[string]string{
				"set.#": "3",
				"set.1": "1",
				"set.2": "2",
				"set.5": "5",
			},
		},

		"set nil": {
			[]string{"set"},
			nil,
			false,
			map[string]string{
				"set.#": "0",
			},
		},

		"set resource": {
			[]string{"setDeep"},
			[]interface{}{
				map[string]interface{}{
					"index": 10,
					"value": "foo",
				},
				map[string]interface{}{
					"index": 50,
					"value": "bar",
				},
			},
			false,
			map[string]string{
				"setDeep.#":        "2",
				"setDeep.10.index": "10",
				"setDeep.10.value": "foo",
				"setDeep.50.index": "50",
				"setDeep.50.value": "bar",
			},
		},

		"set element": {
			[]string{"set", "5"},
			5,
			true,
			map[string]string{},
		},

		"full object": {
			nil,
			map[string]interface{}{
				"string": "foo",
				"list":   []interface{}{"foo", "bar"},
			},
			false,
			map[string]string{
				"string": "foo",
				"list.#": "2",
				"list.0": "foo",
				"list.1": "bar",
			},
		},
	}

	for name, tc := range cases {
		w := &MapFieldWriter{Schema: schema}
		err := w.WriteField(tc.Addr, tc.Value)
		if (err != nil) != tc.Err {
			t.Fatalf("%s: err: %s", name, err)
		}

		actual := w.Map()
		if !reflect.DeepEqual(actual, tc.Out) {
			t.Fatalf("%s: bad: %#v", name, actual)
		}
	}
}
<|endoftext|>


<commit_before>// Package ihexloader encapsulates loading of IHEX files into an Emulator.
package ihexloader

import (
    "github.com/kierdavis/avr/emulator"
    "github.com/kierdavis/ihex-go"
    "io"
)

// LoadIHEX parses an IHEX file from r and loads the program data contained in
// it into em.
func LoadIHEX(em *emulator.Emulator, r io.Reader) (err error) {
    dec := ihex.NewDecoder(r)
    buf := make([]uint16, 0, 8)

    for dec.Scan() {
        rec := dec.Record()
        if rec.Type == ihex.Data {
            buf = buf[:0]
            for i := 0; i+1 < len(rec.Data); i += 2 {
                lo := uint16(rec.Data[i])
                hi := uint16(rec.Data[i+1])
                buf = append(buf, (hi << 8) | lo)
            }

            em.WriteProg(rec.Address, buf)
        }
    }

    return dec.Err()
}
<commit_msg>Rename LoadIHEX to simply Load as it is already qualified by its package name ihexloader<commit_after>// Package ihexloader encapsulates loading of IHEX files into an Emulator.
package ihexloader

import (
    "github.com/kierdavis/avr/emulator"
    "github.com/kierdavis/ihex-go"
    "io"
)

// Load parses an IHEX file from r and loads the program data contained in
// it into em.
func Load(em *emulator.Emulator, r io.Reader) (err error) {
    dec := ihex.NewDecoder(r)
    buf := make([]uint16, 0, 8)

    for dec.Scan() {
        rec := dec.Record()
        if rec.Type == ihex.Data {
            buf = buf[:0]
            for i := 0; i+1 < len(rec.Data); i += 2 {
                lo := uint16(rec.Data[i])
                hi := uint16(rec.Data[i+1])
                buf = append(buf, (hi << 8) | lo)
            }

            em.WriteProg(rec.Address, buf)
        }
    }

    return dec.Err()
}
<|endoftext|>


<commit_before>package repositoriesmanager

import (
	"fmt"

	"github.com/go-gorp/gorp"
	"github.com/mitchellh/mapstructure"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/database"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

//ReceiveEvents has to be launched as a goroutine.
func ReceiveEvents() {
	for {
		e := sdk.Event{}
		cache.Dequeue("events_repositoriesmanager", &e)
		db := database.DBMap(database.DB())
		if db != nil {
			if err := processEvent(db, e); err != nil {
				log.Error("ReceiveEvents> err while processing %s : %v", err, e)
				retryEvent(&e)
			}
			continue
		}
		retryEvent(&e)
	}
}

func retryEvent(e *sdk.Event) {
	e.Attempts++
	if e.Attempts > 2 {
		log.Error("ReceiveEvents> Aborting event processing %v", e)
		return
	}
	cache.Enqueue("events_repositoriesmanager", e)
}

func processEvent(db gorp.SqlExecutor, event sdk.Event) error {
	log.Debug("repositoriesmanager>processEvent> receive: type:%s all: %+v", event.EventType, event)

	if event.EventType != fmt.Sprintf("%T", sdk.EventPipelineBuild{}) {
		return nil
	}

	var eventpb sdk.EventPipelineBuild
	if err := mapstructure.Decode(event.Payload, &eventpb); err != nil {
		log.Error("Error during consumption: %s", err)
		return err
	}

	if eventpb.RepositoryManagerName == "" {
		return nil
	}

	log.Debug("repositoriesmanager>processEvent> event:%+v", event)

	c, erra := AuthorizedClient(db, eventpb.ProjectKey, eventpb.RepositoryManagerName)
	if erra != nil {
		return fmt.Errorf("repositoriesmanager>processEvent> AuthorizedClient (%s, %s) > err:%s", eventpb.ProjectKey, eventpb.RepositoryManagerName, erra)
	}

	if err := c.SetStatus(event); err != nil {
		retryEvent(&event)
		return fmt.Errorf("repositoriesmanager>processEvent> SetStatus > err:%s", err)
	}

	retryEvent(&event)

	return nil
}
<commit_msg>fix (api): error management on retryEvent (#595)<commit_after>package repositoriesmanager

import (
	"fmt"

	"github.com/go-gorp/gorp"
	"github.com/mitchellh/mapstructure"

	"github.com/ovh/cds/engine/api/cache"
	"github.com/ovh/cds/engine/api/database"
	"github.com/ovh/cds/sdk"
	"github.com/ovh/cds/sdk/log"
)

//ReceiveEvents has to be launched as a goroutine.
func ReceiveEvents() {
	for {
		e := sdk.Event{}
		cache.Dequeue("events_repositoriesmanager", &e)
		db := database.DBMap(database.DB())
		if db != nil {
			if err := processEvent(db, e); err != nil {
				log.Error("ReceiveEvents> err while processing error=%s : %v", err, e)
				retryEvent(&e, err)
			}
			continue
		}
		retryEvent(&e, nil)
	}
}

func retryEvent(e *sdk.Event, err error) {
	e.Attempts++
	if e.Attempts > 2 {
		log.Error("ReceiveEvents> Aborting event processing %v: %v", err, e)
		return
	}
	cache.Enqueue("events_repositoriesmanager", e)
}

func processEvent(db gorp.SqlExecutor, event sdk.Event) error {
	log.Debug("repositoriesmanager>processEvent> receive: type:%s all: %+v", event.EventType, event)

	if event.EventType != fmt.Sprintf("%T", sdk.EventPipelineBuild{}) {
		return nil
	}

	var eventpb sdk.EventPipelineBuild
	if err := mapstructure.Decode(event.Payload, &eventpb); err != nil {
		log.Error("Error during consumption: %s", err)
		return err
	}

	if eventpb.RepositoryManagerName == "" {
		return nil
	}

	log.Debug("repositoriesmanager>processEvent> event:%+v", event)

	c, erra := AuthorizedClient(db, eventpb.ProjectKey, eventpb.RepositoryManagerName)
	if erra != nil {
		return fmt.Errorf("repositoriesmanager>processEvent> AuthorizedClient (%s, %s) > err:%s", eventpb.ProjectKey, eventpb.RepositoryManagerName, erra)
	}

	if err := c.SetStatus(event); err != nil {
		retryEvent(&event, err)
		return fmt.Errorf("repositoriesmanager>processEvent> SetStatus > err:%s", err)
	}

	retryEvent(&event, nil)

	return nil
}
<|endoftext|>


<commit_before>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package queue

import (
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
)

const (
	// StatusUpdateFrequency is how many containers should be processed between
	// logs
	StatusUpdateFrequency = 2500
)

var (
	errDuplicate = errors.New("duplicated container")
)

// Jobs tracks a series of jobs that form a DAG of dependencies.
type Jobs struct {
	// db ensures that database updates are atomically updated.
	db *versiondb.Database
	// state writes the job queue to [db].
	state *state
}

// New attempts to create a new job queue from the provided database.
func New(db database.Database) *Jobs {
	vdb := versiondb.New(db)
	return &Jobs{
		db:    vdb,
		state: newState(vdb),
	}
}

// SetParser tells this job queue how to parse jobs from the database.
func (j *Jobs) SetParser(parser Parser) { j.state.parser = parser }

func (j *Jobs) Has(jobID ids.ID) (bool, error) { return j.state.HasJob(jobID) }

// Push adds a new job to the queue.
func (j *Jobs) Push(job Job) (bool, error) {
	jobID := job.ID()
	if has, err := j.state.HasJob(jobID); err != nil {
		return false, fmt.Errorf("failed to check for existing job %s due to %w", jobID, err)
	} else if has {
		return false, nil
	}

	deps, err := job.MissingDependencies()
	if err != nil {
		return false, err
	}
	// Store this job into the database.
	if err := j.state.PutJob(job); err != nil {
		return false, fmt.Errorf("failed to write job due to %w", err)
	}

	if deps.Len() != 0 {
		// This job needs to block on a set of dependencies.
		for depID := range deps {
			if err := j.state.AddDependency(depID, jobID); err != nil {
				return false, fmt.Errorf("failed to add blocking for depID %s, jobID %s", depID, jobID)
			}
		}
		return true, nil
	}
	// This job doesn't have any dependencies, so it should be placed onto the
	// executable stack.
	if err := j.state.AddRunnableJob(jobID); err != nil {
		return false, fmt.Errorf("failed to add %s as a runnable job due to %w", jobID, err)
	}
	return true, nil
}

func (j *Jobs) ExecuteAll(ctx *snow.Context, events ...snow.EventDispatcher) (int, error) {
	numExecuted := 0
	for {
		job, err := j.state.RemoveRunnableJob()
		if err == database.ErrNotFound {
			break
		}
		if err != nil {
			return 0, fmt.Errorf("failed to removing runnable job with %w", err)
		}

		jobID := job.ID()
		ctx.Log.Debug("Executing: %s", jobID)
		if err := job.Execute(); err != nil {
			return 0, fmt.Errorf("failed to execute job %s due to %w", jobID, err)
		}

		dependentIDs, err := j.state.RemoveDependencies(jobID)
		if err != nil {
			return 0, fmt.Errorf("failed to remove blocking jobs for %s due to %w", jobID, err)
		}

		for _, dependentID := range dependentIDs {
			job, err := j.state.GetJob(dependentID)
			if err != nil {
				return 0, fmt.Errorf("failed to get job %s from blocking jobs due to %w", dependentID, err)
			}
			deps, err := job.MissingDependencies()
			if err != nil {
				return 0, fmt.Errorf("failed to get missing dependencies for %s due to %w", dependentID, err)
			}
			if deps.Len() > 0 {
				continue
			}
			if err := j.state.AddRunnableJob(dependentID); err != nil {
				return 0, fmt.Errorf("failed to add %s as a runnable job due to %w", dependentID, err)
			}
		}
		if err := j.Commit(); err != nil {
			return 0, err
		}

		numExecuted++
		if numExecuted%StatusUpdateFrequency == 0 { // Periodically print progress
			ctx.Log.Info("executed %d operations", numExecuted)
		}

		for _, event := range events {
			event.Accept(ctx, job.ID(), job.Bytes())
		}
	}

	ctx.Log.Info("executed %d operations", numExecuted)
	return numExecuted, nil
}

// Commit the versionDB to the underlying database.
func (j *Jobs) Commit() error {
	return j.db.Commit()
}

type JobsWithMissing struct {
	*Jobs

	// keep the missing ID set in memory to avoid unnecessary database reads and
	// writes.
	missingIDs                            ids.Set
	removeFromMissingIDs, addToMissingIDs ids.Set
}

func NewWithMissing(db database.Database) (*JobsWithMissing, error) {
	jobs := &JobsWithMissing{
		Jobs: New(db),
	}

	missingIDs, err := jobs.state.MissingJobIDs()
	jobs.missingIDs.Add(missingIDs...)
	return jobs, err
}

func (jm *JobsWithMissing) Has(jobID ids.ID) (bool, error) {
	if jm.missingIDs.Contains(jobID) {
		return false, nil
	}

	return jm.Jobs.Has(jobID)
}

// Push adds a new job to the queue.
func (jm *JobsWithMissing) Push(job Job) (bool, error) {
	jobID := job.ID()
	if has, err := jm.Has(jobID); err != nil {
		return false, fmt.Errorf("failed to check for existing job %s due to %w", jobID, err)
	} else if has {
		return false, nil
	}

	deps, err := job.MissingDependencies()
	if err != nil {
		return false, err
	}
	// Store this job into the database.
	if err := jm.state.PutJob(job); err != nil {
		return false, fmt.Errorf("failed to write job due to %w", err)
	}

	if deps.Len() != 0 {
		// This job needs to block on a set of dependencies.
		for depID := range deps {
			if err := jm.state.AddDependency(depID, jobID); err != nil {
				return false, fmt.Errorf("failed to add blocking for depID %s, jobID %s", depID, jobID)
			}
		}
		return true, nil
	}
	// This job doesn't have any dependencies, so it should be placed onto the
	// executable stack.
	if err := jm.state.AddRunnableJob(jobID); err != nil {
		return false, fmt.Errorf("failed to add %s as a runnable job due to %w", jobID, err)
	}
	return true, nil
}

// AddMissingID adds [jobID] to missingIDs
func (jm *JobsWithMissing) AddMissingID(jobIDs ...ids.ID) {
	for _, jobID := range jobIDs {
		if !jm.missingIDs.Contains(jobID) {
			jm.missingIDs.Add(jobID)
			jm.addToMissingIDs.Add(jobID)
			jm.removeFromMissingIDs.Remove(jobID)
		}
	}
}

// RemoveMissingID removes [jobID] from missingIDs
func (jm *JobsWithMissing) RemoveMissingID(jobIDs ...ids.ID) {
	for _, jobID := range jobIDs {
		if jm.missingIDs.Contains(jobID) {
			jm.missingIDs.Remove(jobID)
			jm.addToMissingIDs.Remove(jobID)
			jm.removeFromMissingIDs.Add(jobID)
		}
	}
}

func (jm *JobsWithMissing) MissingIDs() []ids.ID { return jm.missingIDs.List() }

func (jm *JobsWithMissing) NumMissingIDs() int { return jm.missingIDs.Len() }

// Commit the versionDB to the underlying database.
func (jm *JobsWithMissing) Commit() error {
	if jm.addToMissingIDs.Len() != 0 {
		if err := jm.state.AddMissingJobIDs(jm.addToMissingIDs); err != nil {
			return err
		}
		jm.addToMissingIDs.Clear()
	}
	if jm.removeFromMissingIDs.Len() != 0 {
		if err := jm.state.RemoveMissingJobIDs(jm.removeFromMissingIDs); err != nil {
			return err
		}
		jm.removeFromMissingIDs.Clear()
	}
	return jm.Jobs.Commit()
}
<commit_msg>Fix linting<commit_after>// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package queue

import (
	"fmt"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/versiondb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
)

const (
	// StatusUpdateFrequency is how many containers should be processed between
	// logs
	StatusUpdateFrequency = 2500
)

// Jobs tracks a series of jobs that form a DAG of dependencies.
type Jobs struct {
	// db ensures that database updates are atomically updated.
	db *versiondb.Database
	// state writes the job queue to [db].
	state *state
}

// New attempts to create a new job queue from the provided database.
func New(db database.Database) *Jobs {
	vdb := versiondb.New(db)
	return &Jobs{
		db:    vdb,
		state: newState(vdb),
	}
}

// SetParser tells this job queue how to parse jobs from the database.
func (j *Jobs) SetParser(parser Parser) { j.state.parser = parser }

func (j *Jobs) Has(jobID ids.ID) (bool, error) { return j.state.HasJob(jobID) }

// Push adds a new job to the queue.
func (j *Jobs) Push(job Job) (bool, error) {
	jobID := job.ID()
	if has, err := j.state.HasJob(jobID); err != nil {
		return false, fmt.Errorf("failed to check for existing job %s due to %w", jobID, err)
	} else if has {
		return false, nil
	}

	deps, err := job.MissingDependencies()
	if err != nil {
		return false, err
	}
	// Store this job into the database.
	if err := j.state.PutJob(job); err != nil {
		return false, fmt.Errorf("failed to write job due to %w", err)
	}

	if deps.Len() != 0 {
		// This job needs to block on a set of dependencies.
		for depID := range deps {
			if err := j.state.AddDependency(depID, jobID); err != nil {
				return false, fmt.Errorf("failed to add blocking for depID %s, jobID %s", depID, jobID)
			}
		}
		return true, nil
	}
	// This job doesn't have any dependencies, so it should be placed onto the
	// executable stack.
	if err := j.state.AddRunnableJob(jobID); err != nil {
		return false, fmt.Errorf("failed to add %s as a runnable job due to %w", jobID, err)
	}
	return true, nil
}

func (j *Jobs) ExecuteAll(ctx *snow.Context, events ...snow.EventDispatcher) (int, error) {
	numExecuted := 0
	for {
		job, err := j.state.RemoveRunnableJob()
		if err == database.ErrNotFound {
			break
		}
		if err != nil {
			return 0, fmt.Errorf("failed to removing runnable job with %w", err)
		}

		jobID := job.ID()
		ctx.Log.Debug("Executing: %s", jobID)
		if err := job.Execute(); err != nil {
			return 0, fmt.Errorf("failed to execute job %s due to %w", jobID, err)
		}

		dependentIDs, err := j.state.RemoveDependencies(jobID)
		if err != nil {
			return 0, fmt.Errorf("failed to remove blocking jobs for %s due to %w", jobID, err)
		}

		for _, dependentID := range dependentIDs {
			job, err := j.state.GetJob(dependentID)
			if err != nil {
				return 0, fmt.Errorf("failed to get job %s from blocking jobs due to %w", dependentID, err)
			}
			deps, err := job.MissingDependencies()
			if err != nil {
				return 0, fmt.Errorf("failed to get missing dependencies for %s due to %w", dependentID, err)
			}
			if deps.Len() > 0 {
				continue
			}
			if err := j.state.AddRunnableJob(dependentID); err != nil {
				return 0, fmt.Errorf("failed to add %s as a runnable job due to %w", dependentID, err)
			}
		}
		if err := j.Commit(); err != nil {
			return 0, err
		}

		numExecuted++
		if numExecuted%StatusUpdateFrequency == 0 { // Periodically print progress
			ctx.Log.Info("executed %d operations", numExecuted)
		}

		for _, event := range events {
			event.Accept(ctx, job.ID(), job.Bytes())
		}
	}

	ctx.Log.Info("executed %d operations", numExecuted)
	return numExecuted, nil
}

// Commit the versionDB to the underlying database.
func (j *Jobs) Commit() error {
	return j.db.Commit()
}

type JobsWithMissing struct {
	*Jobs

	// keep the missing ID set in memory to avoid unnecessary database reads and
	// writes.
	missingIDs                            ids.Set
	removeFromMissingIDs, addToMissingIDs ids.Set
}

func NewWithMissing(db database.Database) (*JobsWithMissing, error) {
	jobs := &JobsWithMissing{
		Jobs: New(db),
	}

	missingIDs, err := jobs.state.MissingJobIDs()
	jobs.missingIDs.Add(missingIDs...)
	return jobs, err
}

func (jm *JobsWithMissing) Has(jobID ids.ID) (bool, error) {
	if jm.missingIDs.Contains(jobID) {
		return false, nil
	}

	return jm.Jobs.Has(jobID)
}

// Push adds a new job to the queue.
func (jm *JobsWithMissing) Push(job Job) (bool, error) {
	jobID := job.ID()
	if has, err := jm.Has(jobID); err != nil {
		return false, fmt.Errorf("failed to check for existing job %s due to %w", jobID, err)
	} else if has {
		return false, nil
	}

	deps, err := job.MissingDependencies()
	if err != nil {
		return false, err
	}
	// Store this job into the database.
	if err := jm.state.PutJob(job); err != nil {
		return false, fmt.Errorf("failed to write job due to %w", err)
	}

	if deps.Len() != 0 {
		// This job needs to block on a set of dependencies.
		for depID := range deps {
			if err := jm.state.AddDependency(depID, jobID); err != nil {
				return false, fmt.Errorf("failed to add blocking for depID %s, jobID %s", depID, jobID)
			}
		}
		return true, nil
	}
	// This job doesn't have any dependencies, so it should be placed onto the
	// executable stack.
	if err := jm.state.AddRunnableJob(jobID); err != nil {
		return false, fmt.Errorf("failed to add %s as a runnable job due to %w", jobID, err)
	}
	return true, nil
}

// AddMissingID adds [jobID] to missingIDs
func (jm *JobsWithMissing) AddMissingID(jobIDs ...ids.ID) {
	for _, jobID := range jobIDs {
		if !jm.missingIDs.Contains(jobID) {
			jm.missingIDs.Add(jobID)
			jm.addToMissingIDs.Add(jobID)
			jm.removeFromMissingIDs.Remove(jobID)
		}
	}
}

// RemoveMissingID removes [jobID] from missingIDs
func (jm *JobsWithMissing) RemoveMissingID(jobIDs ...ids.ID) {
	for _, jobID := range jobIDs {
		if jm.missingIDs.Contains(jobID) {
			jm.missingIDs.Remove(jobID)
			jm.addToMissingIDs.Remove(jobID)
			jm.removeFromMissingIDs.Add(jobID)
		}
	}
}

func (jm *JobsWithMissing) MissingIDs() []ids.ID { return jm.missingIDs.List() }

func (jm *JobsWithMissing) NumMissingIDs() int { return jm.missingIDs.Len() }

// Commit the versionDB to the underlying database.
func (jm *JobsWithMissing) Commit() error {
	if jm.addToMissingIDs.Len() != 0 {
		if err := jm.state.AddMissingJobIDs(jm.addToMissingIDs); err != nil {
			return err
		}
		jm.addToMissingIDs.Clear()
	}
	if jm.removeFromMissingIDs.Len() != 0 {
		if err := jm.state.RemoveMissingJobIDs(jm.removeFromMissingIDs); err != nil {
			return err
		}
		jm.removeFromMissingIDs.Clear()
	}
	return jm.Jobs.Commit()
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package openstack

import (
	"errors"
	"fmt"
	"net/url"

	"github.com/golang/glog"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/pagination"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

type Instances struct {
	compute *gophercloud.ServiceClient
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := os.NewComputeV2()
	if err != nil {
		return nil, false
	}

	glog.V(1).Info("Claiming to support Instances")

	return &Instances{compute}, true
}

func (i *Instances) List(name_filter string) ([]types.NodeName, error) {
	glog.V(4).Infof("openstack List(%v) called", name_filter)

	opts := servers.ListOpts{
		Name:   name_filter,
		Status: "ACTIVE",
	}
	pager := servers.List(i.compute, opts)

	ret := make([]types.NodeName, 0)
	err := pager.EachPage(func(page pagination.Page) (bool, error) {
		sList, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}
		for i := range sList {
			ret = append(ret, mapServerToNodeName(&sList[i]))
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Found %v instances matching %v: %v",
		len(ret), name_filter, ret)

	return ret, nil
}

// Implementation of Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
	md, err := getMetadata()
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Name), nil
}

func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
	return errors.New("unimplemented")
}

func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	addresses, err := nodeAddresses(server)
	if err != nil {
		return []v1.NodeAddress{}, err
	}

	return addresses, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	if len(os.localInstanceID) == 0 {
		id, err := readInstanceID()
		if err != nil {
			return "", err
		}
		os.localInstanceID = id
	}
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) InstanceTypeByProviderID(providerID string) (string, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

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

	server, err := servers.Get(i.compute, instanceID).Extract()

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

	return srvInstanceType(server)
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)

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

	return srvInstanceType(srv)
}

func srvInstanceType(srv *servers.Server) (string, error) {
	keys := []string{"name", "id", "original_name"}
	for _, key := range keys {
		val, found := srv.Flavor[key]
		if found {
			flavor, ok := val.(string)
			if ok {
				return flavor, nil
			}
		}
	}
	return "", fmt.Errorf("flavor name/id not found")
}

func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
	parsedID, err := url.Parse(providerID)
	if err != nil {
		return "", err
	}
	if parsedID.Scheme != ProviderName {
		return "", fmt.Errorf("unrecognized provider %q", parsedID.Scheme)
	}

	return parsedID.Host, nil
}
<commit_msg>Mark the volumes as detached when node does not exist<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package openstack

import (
	"errors"
	"fmt"
	"net/url"

	"github.com/golang/glog"
	"github.com/gophercloud/gophercloud"
	"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
	"github.com/gophercloud/gophercloud/pagination"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/kubernetes/pkg/cloudprovider"
)

type Instances struct {
	compute *gophercloud.ServiceClient
}

// Instances returns an implementation of Instances for OpenStack.
func (os *OpenStack) Instances() (cloudprovider.Instances, bool) {
	glog.V(4).Info("openstack.Instances() called")

	compute, err := os.NewComputeV2()
	if err != nil {
		return nil, false
	}

	glog.V(1).Info("Claiming to support Instances")

	return &Instances{compute}, true
}

func (i *Instances) List(name_filter string) ([]types.NodeName, error) {
	glog.V(4).Infof("openstack List(%v) called", name_filter)

	opts := servers.ListOpts{
		Name:   name_filter,
		Status: "ACTIVE",
	}
	pager := servers.List(i.compute, opts)

	ret := make([]types.NodeName, 0)
	err := pager.EachPage(func(page pagination.Page) (bool, error) {
		sList, err := servers.ExtractServers(page)
		if err != nil {
			return false, err
		}
		for i := range sList {
			ret = append(ret, mapServerToNodeName(&sList[i]))
		}
		return true, nil
	})
	if err != nil {
		return nil, err
	}

	glog.V(3).Infof("Found %v instances matching %v: %v",
		len(ret), name_filter, ret)

	return ret, nil
}

// Implementation of Instances.CurrentNodeName
// Note this is *not* necessarily the same as hostname.
func (i *Instances) CurrentNodeName(hostname string) (types.NodeName, error) {
	md, err := getMetadata()
	if err != nil {
		return "", err
	}
	return types.NodeName(md.Name), nil
}

func (i *Instances) AddSSHKeyToAllInstances(user string, keyData []byte) error {
	return errors.New("unimplemented")
}

func (i *Instances) NodeAddresses(name types.NodeName) ([]v1.NodeAddress, error) {
	glog.V(4).Infof("NodeAddresses(%v) called", name)

	addrs, err := getAddressesByName(i.compute, name)
	if err != nil {
		return nil, err
	}

	glog.V(4).Infof("NodeAddresses(%v) => %v", name, addrs)
	return addrs, nil
}

// NodeAddressesByProviderID returns the node addresses of an instances with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) NodeAddressesByProviderID(providerID string) ([]v1.NodeAddress, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	server, err := servers.Get(i.compute, instanceID).Extract()

	if err != nil {
		return []v1.NodeAddress{}, err
	}

	addresses, err := nodeAddresses(server)
	if err != nil {
		return []v1.NodeAddress{}, err
	}

	return addresses, nil
}

// ExternalID returns the cloud provider ID of the specified instance (deprecated).
func (i *Instances) ExternalID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	return srv.ID, nil
}

// InstanceID returns the kubelet's cloud provider ID.
func (os *OpenStack) InstanceID() (string, error) {
	if len(os.localInstanceID) == 0 {
		id, err := readInstanceID()
		if err != nil {
			return "", err
		}
		os.localInstanceID = id
	}
	return os.localInstanceID, nil
}

// InstanceID returns the cloud provider ID of the specified instance.
func (i *Instances) InstanceID(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)
	if err != nil {
		if err == ErrNotFound {
			return "", cloudprovider.InstanceNotFound
		}
		return "", err
	}
	// In the future it is possible to also return an endpoint as:
	// <endpoint>/<instanceid>
	return "/" + srv.ID, nil
}

// InstanceTypeByProviderID returns the cloudprovider instance type of the node with the specified unique providerID
// This method will not be called from the node that is requesting this ID. i.e. metadata service
// and other local methods cannot be used here
func (i *Instances) InstanceTypeByProviderID(providerID string) (string, error) {
	instanceID, err := instanceIDFromProviderID(providerID)

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

	server, err := servers.Get(i.compute, instanceID).Extract()

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

	return srvInstanceType(server)
}

// InstanceType returns the type of the specified instance.
func (i *Instances) InstanceType(name types.NodeName) (string, error) {
	srv, err := getServerByName(i.compute, name)

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

	return srvInstanceType(srv)
}

func srvInstanceType(srv *servers.Server) (string, error) {
	keys := []string{"name", "id", "original_name"}
	for _, key := range keys {
		val, found := srv.Flavor[key]
		if found {
			flavor, ok := val.(string)
			if ok {
				return flavor, nil
			}
		}
	}
	return "", fmt.Errorf("flavor name/id not found")
}

func instanceIDFromProviderID(providerID string) (instanceID string, err error) {
	parsedID, err := url.Parse(providerID)
	if err != nil {
		return "", err
	}
	if parsedID.Scheme != ProviderName {
		return "", fmt.Errorf("unrecognized provider %q", parsedID.Scheme)
	}

	return parsedID.Host, nil
}
<|endoftext|>