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

package manual

import (
	"errors"
	"fmt"

	"launchpad.net/juju-core/agent"
	"launchpad.net/juju-core/environs"
	envtools "launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/tools"
	"launchpad.net/juju-core/worker/localstorage"
)

const BootstrapInstanceId = instance.Id(manualInstancePrefix)

// LocalStorageEnviron is an Environ where the bootstrap node
// manages its own local storage.
type LocalStorageEnviron interface {
	environs.Environ
	environs.BootstrapStorager
	localstorage.LocalStorageConfig
}

type BootstrapArgs struct {
	Host          string
	Environ       LocalStorageEnviron
	MachineId     string
	PossibleTools tools.List
}

// TODO(axw) make this configurable?
const dataDir = "/var/lib/juju"

func errMachineIdInvalid(machineId string) error {
	return fmt.Errorf("%q is not a valid machine ID", machineId)
}

// NewManualBootstrapEnviron wraps a LocalStorageEnviron with another which
// overrides the Bootstrap method; when Bootstrap is invoked, the specified
// host will be manually bootstrapped.
func Bootstrap(args BootstrapArgs) (err error) {
	if args.Host == "" {
		return errors.New("host argument is empty")
	}
	if args.Environ == nil {
		return errors.New("environ argument is nil")
	}
	if !names.IsMachine(args.MachineId) {
		return errMachineIdInvalid(args.MachineId)
	}

	provisioned, err := checkProvisioned(args.Host)
	if err != nil {
		return fmt.Errorf("failed to check provisioned status: %v", err)
	}
	if provisioned {
		return ErrProvisioned
	}

	bootstrapStorage, err := args.Environ.BootstrapStorage()
	if err != nil {
		return err
	}

	hc, series, err := detectSeriesAndHardwareCharacteristics(args.Host)
	if err != nil {
		return fmt.Errorf("error detecting hardware characteristics: %v", err)
	}

	// Filter tools based on detected series/arch.
	logger.Infof("Filtering possible tools: %v", args.PossibleTools)
	possibleTools, err := args.PossibleTools.Match(tools.Filter{
		Arch:   *hc.Arch,
		Series: series,
	})
	if err != nil {
		return err
	}

	// Store the state file. If provisioning fails, we'll remove the file.
	logger.Infof("Saving bootstrap state file to bootstrap storage")
	err = provider.SaveState(
		bootstrapStorage,
		&provider.BootstrapState{
			StateInstances:  []instance.Id{BootstrapInstanceId},
			Characteristics: []instance.HardwareCharacteristics{hc},
		},
	)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			logger.Errorf("bootstrapping failed, removing state file: %v", err)
			bootstrapStorage.Remove(provider.StateFile)
		}
	}()

	// Get a file:// scheme tools URL for the tools, which will have been
	// copied to the remote machine's storage directory.
	tools := *possibleTools[0]
	storageDir := args.Environ.StorageDir()
	toolsStorageName := envtools.StorageName(tools.Version)
	tools.URL = fmt.Sprintf("file://%s/%s", storageDir, toolsStorageName)

	// Add the local storage configuration.
	agentEnv := map[string]string{
		agent.StorageAddr:       args.Environ.StorageAddr(),
		agent.StorageDir:        storageDir,
		agent.SharedStorageAddr: args.Environ.SharedStorageAddr(),
		agent.SharedStorageDir:  args.Environ.SharedStorageDir(),
	}

	// Finally, provision the machine agent.
	stateFileURL := fmt.Sprintf("file://%s/%s", storageDir, provider.StateFile)
	err = provisionMachineAgent(provisionMachineAgentArgs{
		host:          args.Host,
		dataDir:       dataDir,
		environConfig: args.Environ.Config(),
		stateFileURL:  stateFileURL,
		machineId:     args.MachineId,
		bootstrap:     true,
		nonce:         state.BootstrapNonce,
		tools:         &tools,
		agentEnv:      agentEnv,
	})
	return err<commit_msg>environs/manual: use new tools prefix<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package manual

import (
	"errors"
	"fmt"

	"launchpad.net/juju-core/agent"
	"launchpad.net/juju-core/environs"
	envtools "launchpad.net/juju-core/environs/tools"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/provider"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/tools"
	"launchpad.net/juju-core/worker/localstorage"
)

const BootstrapInstanceId = instance.Id(manualInstancePrefix)

// LocalStorageEnviron is an Environ where the bootstrap node
// manages its own local storage.
type LocalStorageEnviron interface {
	environs.Environ
	environs.BootstrapStorager
	localstorage.LocalStorageConfig
}

type BootstrapArgs struct {
	Host          string
	Environ       LocalStorageEnviron
	MachineId     string
	PossibleTools tools.List
}

// TODO(axw) make this configurable?
const dataDir = "/var/lib/juju"

func errMachineIdInvalid(machineId string) error {
	return fmt.Errorf("%q is not a valid machine ID", machineId)
}

// NewManualBootstrapEnviron wraps a LocalStorageEnviron with another which
// overrides the Bootstrap method; when Bootstrap is invoked, the specified
// host will be manually bootstrapped.
func Bootstrap(args BootstrapArgs) (err error) {
	if args.Host == "" {
		return errors.New("host argument is empty")
	}
	if args.Environ == nil {
		return errors.New("environ argument is nil")
	}
	if !names.IsMachine(args.MachineId) {
		return errMachineIdInvalid(args.MachineId)
	}

	provisioned, err := checkProvisioned(args.Host)
	if err != nil {
		return fmt.Errorf("failed to check provisioned status: %v", err)
	}
	if provisioned {
		return ErrProvisioned
	}

	bootstrapStorage, err := args.Environ.BootstrapStorage()
	if err != nil {
		return err
	}

	hc, series, err := detectSeriesAndHardwareCharacteristics(args.Host)
	if err != nil {
		return fmt.Errorf("error detecting hardware characteristics: %v", err)
	}

	// Filter tools based on detected series/arch.
	logger.Infof("Filtering possible tools: %v", args.PossibleTools)
	possibleTools, err := args.PossibleTools.Match(tools.Filter{
		Arch:   *hc.Arch,
		Series: series,
	})
	if err != nil {
		return err
	}

	// Store the state file. If provisioning fails, we'll remove the file.
	logger.Infof("Saving bootstrap state file to bootstrap storage")
	err = provider.SaveState(
		bootstrapStorage,
		&provider.BootstrapState{
			StateInstances:  []instance.Id{BootstrapInstanceId},
			Characteristics: []instance.HardwareCharacteristics{hc},
		},
	)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			logger.Errorf("bootstrapping failed, removing state file: %v", err)
			bootstrapStorage.Remove(provider.StateFile)
		}
	}()

	// Set the new tools prefix so StorageName returns the right thing.
	restore := envtools.SetToolPrefix(envtools.NewToolPrefix)
	defer restore()

	// Get a file:// scheme tools URL for the tools, which will have been
	// copied to the remote machine's storage directory.
	tools := *possibleTools[0]
	storageDir := args.Environ.StorageDir()
	toolsStorageName := envtools.StorageName(tools.Version)
	tools.URL = fmt.Sprintf("file://%s/%s", storageDir, toolsStorageName)

	// Add the local storage configuration.
	agentEnv := map[string]string{
		agent.StorageAddr:       args.Environ.StorageAddr(),
		agent.StorageDir:        storageDir,
		agent.SharedStorageAddr: args.Environ.SharedStorageAddr(),
		agent.SharedStorageDir:  args.Environ.SharedStorageDir(),
	}

	// Finally, provision the machine agent.
	stateFileURL := fmt.Sprintf("file://%s/%s", storageDir, provider.StateFile)
	err = provisionMachineAgent(provisionMachineAgentArgs{
		host:          args.Host,
		dataDir:       dataDir,
		environConfig: args.Environ.Config(),
		stateFileURL:  stateFileURL,
		machineId:     args.MachineId,
		bootstrap:     true,
		nonce:         state.BootstrapNonce,
		tools:         &tools,
		agentEnv:      agentEnv,
	})
	return err
{'fraction_non_alphanumeric': 0.10961150353178607, 'fraction_numerical': 0.0015136226034308778, 'mean_word_length': 4.122093023255814, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, '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': '2106529', 'n_tokens_mistral': 2659, 'n_tokens_neox': 2462, 'n_words': 640}