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

package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"sort"

	"launchpad.net/juju-core/charm/hooks"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/rpc"
	unitdebug "launchpad.net/juju-core/worker/uniter/debug"
)

// DebugHooksCommand is responsible for launching a ssh shell on a given unit or machine.
type DebugHooksCommand struct {
	SSHCommand
	hooks []string
}

const debugHooksDoc = `
Interactively debug a hook remotely on a service unit.
`

func (c *DebugHooksCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "debug-hooks",
		Args:    "<unit name> [hook names]",
		Purpose: "launch a tmux session to debug a hook",
		Doc:     debugHooksDoc,
	}
}

func (c *DebugHooksCommand) Init(args []string) error {
	if len(args) < 1 {
		return errors.New("no unit name specified")
	}
	c.Target = args[0]
	if !names.IsUnit(c.Target) {
		return fmt.Errorf("%q is not a valid unit name", c.Target)
	}

	// If any of the hooks is "*", then debug all hooks.
	c.hooks = append([]string{}, args[1:]...)
	for _, h := range c.hooks {
		if h == "*" {
			c.hooks = nil
			break
		}
	}
	return nil
}

// getRelationNames1dot16 gets the list of relation hooks directly from the
// database, in a fashion compatible with the API server in Juju 1.16 (which
// doesn't have the ServiceCharmRelations API). This function can be removed
// when we no longer maintain compatibility with 1.16
func (c *DebugHooksCommand) getRelationNames1dot16() ([]string, error) {
	err := c.ensureRawConn()
	if err != nil {
		return nil, err
	}
	unit, err := c.rawConn.State.Unit(c.Target)
	if err != nil {
		return nil, err
	}
	service, err := unit.Service()
	if err != nil {
		return nil, err
	}
	endpoints, err := service.Endpoints()
	if err != nil {
		return nil, err
	}
	relations := make([]string, len(endpoints))
	for i, endpoint := range endpoints {
		relations[i] = endpoint.Relation.Name
	}
	return relations, nil
}

func (c *DebugHooksCommand) getRelationNames(serviceName string) ([]string, error) {
	relations, err := c.apiClient.ServiceCharmRelations(serviceName)
	if rpc.IsNoSuchRequest(err) {
		logger.Infof("API server does not support Client.ServiceCharmRelations falling back to 1.16 compatibility mode (direct DB access)")
		return c.getRelationNames1dot16()
	}
	if err != nil {
		return nil, err
	}
	return relations, err
}

func (c *DebugHooksCommand) validateHooks() error {
	if len(c.hooks) == 0 {
		return nil
	}
	service := names.UnitService(c.Target)
	relations, err := c.getRelationNames(service)
	if err != nil {
		return err
	}

	validHooks := make(map[string]bool)
	for _, hook := range hooks.UnitHooks() {
		validHooks[string(hook)] = true
	}
	for _, relation := range relations {
		for _, hook := range hooks.RelationHooks() {
			hook := fmt.Sprintf("%s-%s", relation, hook)
			validHooks[hook] = true
		}
	}
	for _, hook := range c.hooks {
		if !validHooks[hook] {
			names := make([]string, 0, len(validHooks))
			for hookName, _ := range validHooks {
				names = append(names, hookName)
			}
			sort.Strings(names)
			logger.Infof("unknown hook %s, valid hook names: %v", c.Target, names)
			return fmt.Errorf("unit %q does not contain hook %q", c.Target, hook)
		}
	}<commit_msg>axw pointed out that I was logging the wrong thing.<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package main

import (
	"encoding/base64"
	"errors"
	"fmt"
	"sort"

	"launchpad.net/juju-core/charm/hooks"
	"launchpad.net/juju-core/cmd"
	"launchpad.net/juju-core/names"
	"launchpad.net/juju-core/rpc"
	unitdebug "launchpad.net/juju-core/worker/uniter/debug"
)

// DebugHooksCommand is responsible for launching a ssh shell on a given unit or machine.
type DebugHooksCommand struct {
	SSHCommand
	hooks []string
}

const debugHooksDoc = `
Interactively debug a hook remotely on a service unit.
`

func (c *DebugHooksCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "debug-hooks",
		Args:    "<unit name> [hook names]",
		Purpose: "launch a tmux session to debug a hook",
		Doc:     debugHooksDoc,
	}
}

func (c *DebugHooksCommand) Init(args []string) error {
	if len(args) < 1 {
		return errors.New("no unit name specified")
	}
	c.Target = args[0]
	if !names.IsUnit(c.Target) {
		return fmt.Errorf("%q is not a valid unit name", c.Target)
	}

	// If any of the hooks is "*", then debug all hooks.
	c.hooks = append([]string{}, args[1:]...)
	for _, h := range c.hooks {
		if h == "*" {
			c.hooks = nil
			break
		}
	}
	return nil
}

// getRelationNames1dot16 gets the list of relation hooks directly from the
// database, in a fashion compatible with the API server in Juju 1.16 (which
// doesn't have the ServiceCharmRelations API). This function can be removed
// when we no longer maintain compatibility with 1.16
func (c *DebugHooksCommand) getRelationNames1dot16() ([]string, error) {
	err := c.ensureRawConn()
	if err != nil {
		return nil, err
	}
	unit, err := c.rawConn.State.Unit(c.Target)
	if err != nil {
		return nil, err
	}
	service, err := unit.Service()
	if err != nil {
		return nil, err
	}
	endpoints, err := service.Endpoints()
	if err != nil {
		return nil, err
	}
	relations := make([]string, len(endpoints))
	for i, endpoint := range endpoints {
		relations[i] = endpoint.Relation.Name
	}
	return relations, nil
}

func (c *DebugHooksCommand) getRelationNames(serviceName string) ([]string, error) {
	relations, err := c.apiClient.ServiceCharmRelations(serviceName)
	if rpc.IsNoSuchRequest(err) {
		logger.Infof("API server does not support Client.ServiceCharmRelations falling back to 1.16 compatibility mode (direct DB access)")
		return c.getRelationNames1dot16()
	}
	if err != nil {
		return nil, err
	}
	return relations, err
}

func (c *DebugHooksCommand) validateHooks() error {
	if len(c.hooks) == 0 {
		return nil
	}
	service := names.UnitService(c.Target)
	relations, err := c.getRelationNames(service)
	if err != nil {
		return err
	}

	validHooks := make(map[string]bool)
	for _, hook := range hooks.UnitHooks() {
		validHooks[string(hook)] = true
	}
	for _, relation := range relations {
		for _, hook := range hooks.RelationHooks() {
			hook := fmt.Sprintf("%s-%s", relation, hook)
			validHooks[hook] = true
		}
	}
	for _, hook := range c.hooks {
		if !validHooks[hook] {
			names := make([]string, 0, len(validHooks))
			for hookName, _ := range validHooks {
				names = append(names, hookName)
			}
			sort.Strings(names)
			logger.Infof("unknown hook %s, valid hook names: %v", hook, names)
			return fmt.Errorf("unit %q does not contain hook %q", c.Target, hook)
		}
	}
{'fraction_non_alphanumeric': 0.13042186571598335, 'fraction_numerical': 0.010101010101010102, 'mean_word_length': 3.7549435028248586, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, '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': '5965453', 'n_tokens_mistral': 2503, 'n_tokens_neox': 2315, 'n_words': 703}