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

package context

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/utils/clock"
	"gopkg.in/juju/charm.v6-unstable/hooks"

	"github.com/juju/juju/api/uniter"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/core/leadership"
	"github.com/juju/juju/worker/uniter/hook"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
)

// CommandInfo specifies the information necessary to run a command.
type CommandInfo struct {
	// RelationId is the relation context to execute the commands in.
	RelationId int
	// RemoteUnitName is the remote unit for the relation context.
	RemoteUnitName string
	// ForceRemoteUnit skips unit inference and existence validation.
	ForceRemoteUnit bool
}

// ContextFactory represents a long-lived object that can create execution contexts
// relevant to a specific unit.
type ContextFactory interface {
	// CommandContext creates a new context for running a juju command.
	CommandContext(commandInfo CommandInfo) (*HookContext, error)

	// HookContext creates a new context for running a juju hook.
	HookContext(hookInfo hook.Info) (*HookContext, error)

	// ActionContext creates a new context for running a juju action.
	ActionContext(actionData *ActionData) (*HookContext, error)
}

// StorageContextAccessor is an interface providing access to StorageContexts
// for a jujuc.Context.
type StorageContextAccessor interface {

	// StorageTags returns the tags of storage instances attached to
	// the unit.
	StorageTags() ([]names.StorageTag, error)

	// Storage returns the jujuc.ContextStorageAttachment with the
	// supplied tag if it was found, and whether it was found.
	Storage(names.StorageTag) (jujuc.ContextStorageAttachment, error)
}

// RelationsFunc is used to get snapshots of relation membership at context
// creation time.
type RelationsFunc func() map[int]*RelationInfo

type contextFactory struct {
	// API connection fields; unit should be deprecated, but isn't yet.
	unit    *uniter.Unit
	state   *uniter.State
	tracker leadership.Tracker

	// Fields that shouldn't change in a factory's lifetime.
	paths      Paths
	modelUUID  string
	envName    string
	machineTag names.MachineTag
	storage    StorageContextAccessor
	clock      clock.Clock

	// Callback to get relation state snapshot.
	getRelationInfos RelationsFunc
	relationCaches   map[int]*RelationCache

	// For generating "unique" context ids.
	rand *rand.Rand
}

// NewContextFactory returns a ContextFactory capable of creating execution contexts backed
// by the supplied unit's supplied API connection.
func NewContextFactory(
	state *uniter.State,
	unitTag names.UnitTag,
	tracker leadership.Tracker,
	getRelationInfos RelationsFunc,
	storage StorageContextAccessor,
	paths Paths,
	clock clock.Clock,
) (
	ContextFactory, error,
) {
	unit, err := state.Unit(unitTag)
	if err != nil {
		return nil, errors.Trace(err)
	}
	machineTag, err := unit.AssignedMachine()
	if err != nil {
		return nil, errors.Trace(err)
	}
	model, err := state.Model()
	if err != nil {
		return nil, errors.Trace(err)
	}
	f := &contextFactory{
		unit:             unit,
		state:            state,
		tracker:          tracker,
		paths:            paths,
		modelUUID:        model.UUID(),
		envName:          model.Name(),
		machineTag:       machineTag,
		getRelationInfos: getRelationInfos,
		relationCaches:   map[int]*RelationCache{},
		storage:          storage,
		rand:             rand.New(rand.NewSource(time.Now().Unix())),
		clock:            clock,
	}
	return f, nil
}

// newId returns a probably-unique identifier for a new context, containing the
// supplied string.
func (f *contextFactory) newId(name string) string {
	return fmt.Sprintf("%s-%s-%d", f.unit.Name(), name, f.rand.Int63())
}

// coreContext creates a new context with all unspecialised fields filled in.
func (f *contextFactory) coreContext() (*HookContext, error) {
	leadershipContext := newLeadershipContext(
		f.state.LeadershipSettings,
		f.tracker,
	)

	zone, err := f.unit.AvailabilityZone()
	if err != nil {
		return nil, errors.Annotatef(err, "cannot obtain availabilty zone for unit %s", f.unit.Name())
	}
	ctx := &HookContext{
		unit:               f.unit,
		state:              f.state,
		LeadershipContext:  leadershipContext,
		uuid:               f.modelUUID,
		envName:            f.envName,
		unitName:           f.unit.Name(),
		assignedMachineTag: f.machineTag,
		relations:          f.getContextRelations(),
		relationId:         -1,
		pendingPorts:       make(map[PortRange]PortRangeInfo),
		storage:            f.storage,
		clock:              f.clock,
		componentDir:       f.paths.ComponentDir,
		componentFuncs:     registeredComponentFuncs,
		availabilityzone:   zone,
	}
	if err := f.updateContext(ctx); err != nil {
		return nil, err
	}
	return ctx, nil
}

// ActionContext is part of the ContextFactory interface.
func (f *contextFactory) ActionContext(actionData *ActionData) (*HookContext, error) {
	if actionData == nil {
		return nil, errors.New("nil actionData specified")
	}
	ctx, err := f.coreContext()
	if err != nil {
		return nil, errors.Trace(err)
	}
	ctx.actionData = actionData<commit_msg>Get zone at factory construction not context.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package context

import (
	"fmt"
	"math/rand"
	"time"

	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/utils/clock"
	"gopkg.in/juju/charm.v6-unstable/hooks"

	"github.com/juju/juju/api/uniter"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/core/leadership"
	"github.com/juju/juju/worker/uniter/hook"
	"github.com/juju/juju/worker/uniter/runner/jujuc"
)

// CommandInfo specifies the information necessary to run a command.
type CommandInfo struct {
	// RelationId is the relation context to execute the commands in.
	RelationId int
	// RemoteUnitName is the remote unit for the relation context.
	RemoteUnitName string
	// ForceRemoteUnit skips unit inference and existence validation.
	ForceRemoteUnit bool
}

// ContextFactory represents a long-lived object that can create execution contexts
// relevant to a specific unit.
type ContextFactory interface {
	// CommandContext creates a new context for running a juju command.
	CommandContext(commandInfo CommandInfo) (*HookContext, error)

	// HookContext creates a new context for running a juju hook.
	HookContext(hookInfo hook.Info) (*HookContext, error)

	// ActionContext creates a new context for running a juju action.
	ActionContext(actionData *ActionData) (*HookContext, error)
}

// StorageContextAccessor is an interface providing access to StorageContexts
// for a jujuc.Context.
type StorageContextAccessor interface {

	// StorageTags returns the tags of storage instances attached to
	// the unit.
	StorageTags() ([]names.StorageTag, error)

	// Storage returns the jujuc.ContextStorageAttachment with the
	// supplied tag if it was found, and whether it was found.
	Storage(names.StorageTag) (jujuc.ContextStorageAttachment, error)
}

// RelationsFunc is used to get snapshots of relation membership at context
// creation time.
type RelationsFunc func() map[int]*RelationInfo

type contextFactory struct {
	// API connection fields; unit should be deprecated, but isn't yet.
	unit    *uniter.Unit
	state   *uniter.State
	tracker leadership.Tracker

	// Fields that shouldn't change in a factory's lifetime.
	paths      Paths
	modelUUID  string
	envName    string
	machineTag names.MachineTag
	storage    StorageContextAccessor
	clock      clock.Clock
	zone       string

	// Callback to get relation state snapshot.
	getRelationInfos RelationsFunc
	relationCaches   map[int]*RelationCache

	// For generating "unique" context ids.
	rand *rand.Rand
}

// NewContextFactory returns a ContextFactory capable of creating execution contexts backed
// by the supplied unit's supplied API connection.
func NewContextFactory(
	state *uniter.State,
	unitTag names.UnitTag,
	tracker leadership.Tracker,
	getRelationInfos RelationsFunc,
	storage StorageContextAccessor,
	paths Paths,
	clock clock.Clock,
) (
	ContextFactory, error,
) {
	unit, err := state.Unit(unitTag)
	if err != nil {
		return nil, errors.Trace(err)
	}
	machineTag, err := unit.AssignedMachine()
	if err != nil {
		return nil, errors.Trace(err)
	}
	model, err := state.Model()
	if err != nil {
		return nil, errors.Trace(err)
	}

	zone, err := unit.AvailabilityZone()
	if err != nil {
		return nil, errors.Trace(err)
	}
	f := &contextFactory{
		unit:             unit,
		state:            state,
		tracker:          tracker,
		paths:            paths,
		modelUUID:        model.UUID(),
		envName:          model.Name(),
		machineTag:       machineTag,
		getRelationInfos: getRelationInfos,
		relationCaches:   map[int]*RelationCache{},
		storage:          storage,
		rand:             rand.New(rand.NewSource(time.Now().Unix())),
		clock:            clock,
		zone:             zone,
	}
	return f, nil
}

// newId returns a probably-unique identifier for a new context, containing the
// supplied string.
func (f *contextFactory) newId(name string) string {
	return fmt.Sprintf("%s-%s-%d", f.unit.Name(), name, f.rand.Int63())
}

// coreContext creates a new context with all unspecialised fields filled in.
func (f *contextFactory) coreContext() (*HookContext, error) {
	leadershipContext := newLeadershipContext(
		f.state.LeadershipSettings,
		f.tracker,
	)
	ctx := &HookContext{
		unit:               f.unit,
		state:              f.state,
		LeadershipContext:  leadershipContext,
		uuid:               f.modelUUID,
		envName:            f.envName,
		unitName:           f.unit.Name(),
		assignedMachineTag: f.machineTag,
		relations:          f.getContextRelations(),
		relationId:         -1,
		pendingPorts:       make(map[PortRange]PortRangeInfo),
		storage:            f.storage,
		clock:              f.clock,
		componentDir:       f.paths.ComponentDir,
		componentFuncs:     registeredComponentFuncs,
		availabilityzone:   f.zone,
	}
	if err := f.updateContext(ctx); err != nil {
		return nil, err
	}
	return ctx, nil
}

// ActionContext is part of the ContextFactory interface.
func (f *contextFactory) ActionContext(actionData *ActionData) (*HookContext, error) {
	if actionData == nil {
		return nil, errors.New("nil actionData specified")
	}
	ctx, err := f.coreContext()
	if err != nil {
		return nil, errors.Trace(err)
	}
	ctx.actionData = actionData
{'fraction_non_alphanumeric': 0.09816183437559219, 'fraction_numerical': 0.0017055144968732233, 'mean_word_length': 3.4554664415365135, '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': '7241246', 'n_tokens_mistral': 3347, 'n_tokens_neox': 3183, 'n_words': 968}