<commit_before>package endly

import (
	"errors"
	"fmt"
	"github.com/viant/endly/common"
	"github.com/viant/toolbox"
	"strings"
	"time"
)

const WorkflowServiceId = "workflow"

type WorkflowRunRequest struct {
	WorkflowURL       string
	Name              string
	Params            map[string]interface{}
	Tasks             string
	PublishParameters bool //publishes parameters name into context state
}

type WorkflowRunResponse struct {
	Name            string
	Data            map[string]interface{}
	SessionInfo     *SessionInfo
	TasksActivities []*WorkflowTaskActivity
}

type WorkflowTaskActivity struct {
	Task              string
	ServiceActivities []*WorkflowServiceActivity
	Skipped           string
}

type WorkflowServiceActivity struct {
	Service         string
	Action          string
	ServiceRequest  interface{}
	ServiceResponse interface{}
	Error           string
	Skipped         string
}

type WorkflowRegisterRequest struct {
	Workflow *Workflow
}

type WorkflowLoadRequest struct {
	Source *Resource
}

type WorkflowService struct {
	*AbstractService
	dao      *WorkflowDao
	registry map[string]*Workflow
}

func (s *WorkflowService) Register(workflow *Workflow) error {
	err := workflow.Validate()
	if err != nil {
		return err
	}
	s.registry[workflow.Name] = workflow
	return nil
}

func (s *WorkflowService) HasWorkflow(name string) bool {
	_, found := s.registry[name]
	return found
}

func (s *WorkflowService) Workflow(name string) (*Workflow, error) {
	if result, found := s.registry[name]; found {
		return result, nil
	}
	return nil, fmt.Errorf("Failed to lookup workflow: %v", name)
}

func (s *WorkflowService) evaluateRunCriteria(context *Context, criteria string) (bool, error) {

	if criteria == "" {
		return true, nil
	}

	colonPosition := strings.Index(criteria, ":")
	if colonPosition == -1 {
		return true, nil
	}
	fragments := strings.Split(criteria, ":")

	actualOperand := context.Expand(strings.TrimSpace(fragments[0]))
	expectedOperand := context.Expand(strings.TrimSpace(fragments[1]))
	validator := &Validator{}
	var result, err = validator.Check(expectedOperand, actualOperand)
	return result, err
}

func isTaskAllowed(candidate *WorkflowTask, request *WorkflowRunRequest) (bool, map[int]bool) {

	if request.Tasks == "" {
		return true, nil
	}
	var actions map[int]bool
	var encodedTask []string
	tasks := strings.Split(request.Tasks, ",")
	for _, task := range tasks {
		encodedTask = nil
		var taskName = task
		if !strings.Contains(task, "=") {
			encodedTask = strings.Split(task, "=")
			taskName = encodedTask[0]
		}
		if taskName == candidate.Name {
			if len(encodedTask) == 2 {
				actions = make(map[int]bool)
				for _, allowedIndex := range strings.Split(encodedTask[1], ":") {
					actions[toolbox.AsInt(allowedIndex)] = true
				}
			}
			return true, actions
		}
	}
	return false, nil
}

func (s *WorkflowService) loadWorkflowIfNeeded(context *Context, name string, URL string) (err error) {
	if !s.HasWorkflow(name) {
		var workflowResource *Resource
		if URL != "" {
			workflowResource, err = NewResource(URL)
			if err != nil {
				return err
			}
		} else {
			workflowResource, err = NewEndlyRepoResource(context, fmt.Sprintf("workflow/%v.csv", name))
			if err != nil {
				return err
			}
		}
		if err == nil {
			err = s.loadWorkflow(context, &WorkflowLoadRequest{Source: workflowResource})
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *WorkflowService) runWorkflow(upstreamContext *Context, request *WorkflowRunRequest) (*WorkflowRunResponse, error) {
	var err = s.loadWorkflowIfNeeded(upstreamContext, request.Name, request.WorkflowURL)
	if err != nil {
		return nil, err
	}

	manager, err := upstreamContext.Manager()
	if err != nil {
		return nil, err
	}
	workflow, err := s.Workflow(request.Name)
	if err != nil {
		return nil, err
	}

	var response = &WorkflowRunResponse{
		TasksActivities: make([]*WorkflowTaskActivity, 0),
		Data:            make(map[string]interface{}),
	}

	parentWorkflow := upstreamContext.Workflow()
	if parentWorkflow != nil {
		upstreamContext.Put(workflowKey, parentWorkflow)
	} else {
		upstreamContext.Put(workflowKey, workflow)
	}

	context := manager.NewContext(upstreamContext.Context.Clone())
	state := NewDefaultState()
	context.Put(stateKey, &state)
	state.Apply(upstreamContext.State())

	var workflowData = common.Map(workflow.Data)
	state.Put("workflow", workflowData)

	params := buildParamsMap(request, context)

	if request.PublishParameters {
		for key, value := range params {
			state.Put(key, ExpandValue(value, state))
		}
	}
	state.Put("params", params)

	err = workflow.Init.Apply(state, state)
	if err != nil {
		return nil, err
	}
	for _, task := range workflow.Tasks {
		var taskAllowed, allowedServiceActions = isTaskAllowed(task, request)
		if !taskAllowed {
			continue
		}

		var hasAllowedActions = len(allowedServiceActions) > 0
		var taskActivity = &WorkflowTaskActivity{
			Task:              task.Name,
			ServiceActivities: make([]*WorkflowServiceActivity, 0),
		}
		response.TasksActivities = append(response.TasksActivities, taskActivity)
		err = task.Init.Apply(state, state)
		if err != nil {
			return nil, err
		}

		canRun, err := s.evaluateRunCriteria(context, task.RunCriteria)
		if err != nil {
			return nil, err
		}
		if !canRun {
			taskActivity.Skipped = fmt.Sprintf("Does not match run criteria: %v", context.Expand(task.RunCriteria))
			continue
		}

		for i, action := range task.Actions {
			if hasAllowedActions && !allowedServiceActions[i] {
				continue
			}
			serviceActivity := &WorkflowServiceActivity{
				Action:  action.Action,
				Service: action.Service,
			}
			taskActivity.ServiceActivities = append(taskActivity.ServiceActivities, serviceActivity)
			canRun, err := s.evaluateRunCriteria(context, action.RunCriteria)
			if err != nil {
				return nil, err
			}
			if !canRun {
				serviceActivity.Skipped = fmt.Sprintf("Does not match run criteria: %v", context.Expand(action.RunCriteria))
				continue
			}

			err = action.Init.Apply(state, state)
			if err != nil {
				return nil, err
			}
			service, err := context.Service(action.Service)

			if err != nil {
				return nil, err
			}

			expandedRequest := ExpandValue(action.Request, state)
			if !toolbox.IsMap(expandedRequest) {
				return nil, fmt.Errorf("Failed to exaluate request: %v, expected map but had: %T", expandedRequest, expandedRequest)
			}
			requestMap := toolbox.AsMap(expandedRequest)
			serviceRequest, err := service.NewRequest(action.Action)
			if err != nil {
				return nil, err
			}

			serviceActivity.ServiceRequest = serviceRequest
			err = converter.AssignConverted(serviceRequest, requestMap)
			if err != nil {
				return response, fmt.Errorf("Failed to create request %v on %v.%v, %v", requestMap, action.Service, action.Action, err)
			}
			fmt.Printf("=============\n\t\t%v.%v (%v)\n=============\n", action.Service, action.Action, action.Description)
			serviceResponse := service.Run(context, serviceRequest)

			serviceActivity.ServiceResponse = serviceResponse

			if serviceResponse.Error != "" {
				if action.IgnoreError {
					serviceActivity.Error = serviceResponse.Error
				} else {
					return nil, errors.New(serviceResponse.Error)
				}
			}
			var responseMap = make(map[string]interface{})
			if serviceResponse.Response != nil {
				converter.AssignConverted(responseMap, serviceResponse.Response)
			}

			err = action.Post.Apply(common.Map(responseMap), state) //result to task  state
			if err != nil {
				return nil, err
			}
			if action.SleepInMs > 0 {
				time.Sleep(time.Millisecond * time.Duration(action.SleepInMs))
			}
		}
		err = task.Post.Apply(state, state)
		if err != nil {
			return nil, err
		}

	}
	workflow.Post.Apply(state, response.Data) //context -> workflow output
	return response, nil
}
func buildParamsMap(request *WorkflowRunRequest, context *Context) common.Map {
	var params = common.NewMap()
	if len(request.Params) > 0 {
		for k, v := range request.Params {
			if toolbox.IsString(v) {
				params[k] = context.Expand(toolbox.AsString(v))
			} else {
				params[k] = v
			}
		}
	}
	return params
}

func (s *WorkflowService) loadWorkflow(context *Context, request *WorkflowLoadRequest) error {
	workflow, err := s.dao.Load(context, request.Source)
	if err != nil {
		return fmt.Errorf("Failed to load workflow: %v, %v", request.Source, err)
	} else {
		err = s.Register(workflow)
		if err != nil {
			return fmt.Errorf("Failed to register workflow: %v, %v", request.Source, err)
		}
	}
	return nil
}

func (s *WorkflowService) Run(context *Context, request interface{}) *ServiceResponse {
	var response = &ServiceResponse{Status: "ok"}
	var err error
	switch actualRequest := request.(type) {
	case *WorkflowRunRequest:
		response.Response, err = s.runWorkflow(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to run workflow: %v, %v", actualRequest.Name, err)
		}
	case *WorkflowRegisterRequest:
		err := s.Register(actualRequest.Workflow)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to register workflow: %v, %v", actualRequest.Workflow.Name, err)
		}
	case *WorkflowLoadRequest:
		err := s.loadWorkflow(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("%v", err)
		}
	default:
		response.Error = fmt.Sprintf("Unsupported request type: %T", request)
	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

func (s *WorkflowService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "run":
		return &WorkflowRunRequest{}, nil
	case "register":
		return &WorkflowRegisterRequest{}, nil
	case "load":
		return &WorkflowLoadRequest{}, nil
	}
	return s.AbstractService.NewRequest(action)
}

func NewWorkflowService() Service {
	var result = &WorkflowService{
		AbstractService: NewAbstractService(WorkflowServiceId),
		dao:             NewWorkflowDao(),
		registry:        make(map[string]*Workflow),
	}
	result.AbstractService.Service = result
	return result
}

//7.0.81
<commit_msg>udpdated debug info<commit_after>package endly

import (
	"errors"
	"fmt"
	"github.com/viant/endly/common"
	"github.com/viant/toolbox"
	"strings"
	"time"
)

const WorkflowServiceId = "workflow"

type WorkflowRunRequest struct {
	WorkflowURL       string
	Name              string
	Params            map[string]interface{}
	Tasks             string
	PublishParameters bool //publishes parameters name into context state
}

type WorkflowRunResponse struct {
	Name            string
	Data            map[string]interface{}
	SessionInfo     *SessionInfo
	TasksActivities []*WorkflowTaskActivity
}

type WorkflowTaskActivity struct {
	Task              string
	ServiceActivities []*WorkflowServiceActivity
	Skipped           string
}

type WorkflowServiceActivity struct {
	Service         string
	Action          string
	ServiceRequest  interface{}
	ServiceResponse interface{}
	Error           string
	Skipped         string
}

type WorkflowRegisterRequest struct {
	Workflow *Workflow
}

type WorkflowLoadRequest struct {
	Source *Resource
}

type WorkflowService struct {
	*AbstractService
	dao      *WorkflowDao
	registry map[string]*Workflow
}

func (s *WorkflowService) Register(workflow *Workflow) error {
	err := workflow.Validate()
	if err != nil {
		return err
	}
	s.registry[workflow.Name] = workflow
	return nil
}

func (s *WorkflowService) HasWorkflow(name string) bool {
	_, found := s.registry[name]
	return found
}

func (s *WorkflowService) Workflow(name string) (*Workflow, error) {
	if result, found := s.registry[name]; found {
		return result, nil
	}
	return nil, fmt.Errorf("Failed to lookup workflow: %v", name)
}

func (s *WorkflowService) evaluateRunCriteria(context *Context, criteria string) (bool, error) {

	if criteria == "" {
		return true, nil
	}

	colonPosition := strings.Index(criteria, ":")
	if colonPosition == -1 {
		return true, nil
	}
	fragments := strings.Split(criteria, ":")

	actualOperand := context.Expand(strings.TrimSpace(fragments[0]))
	expectedOperand := context.Expand(strings.TrimSpace(fragments[1]))
	validator := &Validator{}
	var result, err = validator.Check(expectedOperand, actualOperand)
	return result, err
}

func isTaskAllowed(candidate *WorkflowTask, request *WorkflowRunRequest) (bool, map[int]bool) {

	if request.Tasks == "" {
		return true, nil
	}
	var actions map[int]bool
	var encodedTask []string
	tasks := strings.Split(request.Tasks, ",")
	for _, task := range tasks {
		encodedTask = nil
		var taskName = task
		if !strings.Contains(task, "=") {
			encodedTask = strings.Split(task, "=")
			taskName = encodedTask[0]
		}
		if taskName == candidate.Name {
			if len(encodedTask) == 2 {
				actions = make(map[int]bool)
				for _, allowedIndex := range strings.Split(encodedTask[1], ":") {
					actions[toolbox.AsInt(allowedIndex)] = true
				}
			}
			return true, actions
		}
	}
	return false, nil
}

func (s *WorkflowService) loadWorkflowIfNeeded(context *Context, name string, URL string) (err error) {
	if !s.HasWorkflow(name) {
		var workflowResource *Resource
		if URL != "" {
			workflowResource, err = NewResource(URL)
			if err != nil {
				return err
			}
		} else {
			workflowResource, err = NewEndlyRepoResource(context, fmt.Sprintf("workflow/%v.csv", name))
			if err != nil {
				return err
			}
		}
		if err == nil {
			err = s.loadWorkflow(context, &WorkflowLoadRequest{Source: workflowResource})
		}
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *WorkflowService) runWorkflow(upstreamContext *Context, request *WorkflowRunRequest) (*WorkflowRunResponse, error) {
	var err = s.loadWorkflowIfNeeded(upstreamContext, request.Name, request.WorkflowURL)
	if err != nil {
		return nil, err
	}

	manager, err := upstreamContext.Manager()
	if err != nil {
		return nil, err
	}
	workflow, err := s.Workflow(request.Name)
	if err != nil {
		return nil, err
	}

	var response = &WorkflowRunResponse{
		TasksActivities: make([]*WorkflowTaskActivity, 0),
		Data:            make(map[string]interface{}),
	}

	parentWorkflow := upstreamContext.Workflow()
	if parentWorkflow != nil {
		upstreamContext.Put(workflowKey, parentWorkflow)
	} else {
		upstreamContext.Put(workflowKey, workflow)
	}

	context := manager.NewContext(upstreamContext.Context.Clone())
	state := NewDefaultState()
	context.Put(stateKey, &state)
	state.Apply(upstreamContext.State())

	var workflowData = common.Map(workflow.Data)
	state.Put("workflow", workflowData)

	params := buildParamsMap(request, context)

	if request.PublishParameters {
		for key, value := range params {
			state.Put(key, ExpandValue(value, state))
		}
	}
	state.Put("params", params)

	err = workflow.Init.Apply(state, state)
	if err != nil {
		return nil, err
	}
	for _, task := range workflow.Tasks {
		var taskAllowed, allowedServiceActions = isTaskAllowed(task, request)
		if !taskAllowed {
			continue
		}

		var hasAllowedActions = len(allowedServiceActions) > 0
		var taskActivity = &WorkflowTaskActivity{
			Task:              task.Name,
			ServiceActivities: make([]*WorkflowServiceActivity, 0),
		}
		response.TasksActivities = append(response.TasksActivities, taskActivity)
		err = task.Init.Apply(state, state)
		if err != nil {
			return nil, err
		}

		canRun, err := s.evaluateRunCriteria(context, task.RunCriteria)
		if err != nil {
			return nil, err
		}
		if !canRun {
			taskActivity.Skipped = fmt.Sprintf("Does not match run criteria: %v", context.Expand(task.RunCriteria))
			continue
		}

		for i, action := range task.Actions {
			if hasAllowedActions && !allowedServiceActions[i] {
				continue
			}
			serviceActivity := &WorkflowServiceActivity{
				Action:  action.Action,
				Service: action.Service,
			}
			taskActivity.ServiceActivities = append(taskActivity.ServiceActivities, serviceActivity)
			canRun, err := s.evaluateRunCriteria(context, action.RunCriteria)
			if err != nil {
				return nil, err
			}
			if !canRun {
				serviceActivity.Skipped = fmt.Sprintf("Does not match run criteria: %v", context.Expand(action.RunCriteria))
				continue
			}

			err = action.Init.Apply(state, state)
			if err != nil {
				return nil, err
			}
			service, err := context.Service(action.Service)

			if err != nil {
				return nil, err
			}

			expandedRequest := ExpandValue(action.Request, state)
			if !toolbox.IsMap(expandedRequest) {
				return nil, fmt.Errorf("Failed to exaluate request: %v, expected map but had: %T", expandedRequest, expandedRequest)
			}
			requestMap := toolbox.AsMap(expandedRequest)
			serviceRequest, err := service.NewRequest(action.Action)
			if err != nil {
				return nil, err
			}

			serviceActivity.ServiceRequest = serviceRequest
			err = converter.AssignConverted(serviceRequest, requestMap)
			if err != nil {
				return response, fmt.Errorf("Failed to create request %v on %v.%v, %v", requestMap, action.Service, action.Action, err)
			}
			fmt.Printf("=============\n\t\t%v.%v (%v)\n=============\n", action.Service, action.Action, action.Description)
			serviceResponse := service.Run(context, serviceRequest)

			serviceActivity.ServiceResponse = serviceResponse

			if serviceResponse.Error != "" {
				fmt.Printf("[error] %v\n", serviceResponse.Error)

				if action.IgnoreError {
					serviceActivity.Error = serviceResponse.Error
				} else {
					return nil, errors.New(serviceResponse.Error)
				}
			}
			var responseMap = make(map[string]interface{})
			if serviceResponse.Response != nil {
				converter.AssignConverted(responseMap, serviceResponse.Response)
			}

			err = action.Post.Apply(common.Map(responseMap), state) //result to task  state
			if err != nil {
				return nil, err
			}
			if action.SleepInMs > 0 {
				fmt.Printf("[sleeping]: %v ms\n", action.SleepInMs)
				time.Sleep(time.Millisecond * time.Duration(action.SleepInMs))
			}
		}
		err = task.Post.Apply(state, state)
		if err != nil {
			return nil, err
		}

	}
	workflow.Post.Apply(state, response.Data) //context -> workflow output
	return response, nil
}
func buildParamsMap(request *WorkflowRunRequest, context *Context) common.Map {
	var params = common.NewMap()
	if len(request.Params) > 0 {
		for k, v := range request.Params {
			if toolbox.IsString(v) {
				params[k] = context.Expand(toolbox.AsString(v))
			} else {
				params[k] = v
			}
		}
	}
	return params
}

func (s *WorkflowService) loadWorkflow(context *Context, request *WorkflowLoadRequest) error {
	workflow, err := s.dao.Load(context, request.Source)
	if err != nil {
		return fmt.Errorf("Failed to load workflow: %v, %v", request.Source, err)
	} else {
		err = s.Register(workflow)
		if err != nil {
			return fmt.Errorf("Failed to register workflow: %v, %v", request.Source, err)
		}
	}
	return nil
}

func (s *WorkflowService) Run(context *Context, request interface{}) *ServiceResponse {
	var response = &ServiceResponse{Status: "ok"}
	var err error
	switch actualRequest := request.(type) {
	case *WorkflowRunRequest:
		response.Response, err = s.runWorkflow(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to run workflow: %v, %v", actualRequest.Name, err)
		}
	case *WorkflowRegisterRequest:
		err := s.Register(actualRequest.Workflow)
		if err != nil {
			response.Error = fmt.Sprintf("Failed to register workflow: %v, %v", actualRequest.Workflow.Name, err)
		}
	case *WorkflowLoadRequest:
		err := s.loadWorkflow(context, actualRequest)
		if err != nil {
			response.Error = fmt.Sprintf("%v", err)
		}
	default:
		response.Error = fmt.Sprintf("Unsupported request type: %T", request)
	}
	if response.Error != "" {
		response.Status = "err"
	}
	return response
}

func (s *WorkflowService) NewRequest(action string) (interface{}, error) {
	switch action {
	case "run":
		return &WorkflowRunRequest{}, nil
	case "register":
		return &WorkflowRegisterRequest{}, nil
	case "load":
		return &WorkflowLoadRequest{}, nil
	}
	return s.AbstractService.NewRequest(action)
}

func NewWorkflowService() Service {
	var result = &WorkflowService{
		AbstractService: NewAbstractService(WorkflowServiceId),
		dao:             NewWorkflowDao(),
		registry:        make(map[string]*Workflow),
	}
	result.AbstractService.Service = result
	return result
}

//7.0.81
<|endoftext|>


<commit_before>package juju

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/log"
	"math/big"
	"os"
	"path/filepath"
	"time"
)

// Bootstrap bootstraps the given environment.  The CA certificate and
// private key in PEM format can be given in caPEM; if this is nil,
// they are read from $HOME/.juju/<environ-name>.pem, or generated and
// written there if the file does not exist.  If uploadTools is true,
// the current version of the juju tools will be uploaded, as documented
// in environs.Environ.Bootstrap.
func Bootstrap(environ environs.Environ, uploadTools bool, caPEM []byte) error {
	if caPEM == nil {
		var err error
		caPEM, err = generateCACert(environ.Name())
		if err != nil {
			return fmt.Errorf("cannot generate CA certificate: %v", err)
		}
	}
	caCert, caKey, err := parseCAPEM(caPEM, true)
	if err != nil {
		return fmt.Errorf("bad CA PEM: %v", err)
	}
	// Generate a new key pair and certificate for
	// the newly bootstrapped instance.
	cert, err := generateCert(environ.Name(), caCert, caKey)
	if err != nil {
		return fmt.Errorf("cannot generate bootstrap certificate: %v", err)
	}
	return environ.Bootstrap(uploadTools, cert)
}

const keyBits = 1024

func generateCACert(envName string) ([]byte, error) {
	// TODO make sure that the environment name cannot
	// contain slashes.
	path := filepath.Join(os.Getenv("HOME"), ".juju", envName+".pem")
	data, err := ioutil.ReadFile(path)
	if err == nil {
		return data, nil
	} else if !os.IsNotExist(err) {
		return nil, err
	}
	priv, err := rsa.GenerateKey(rand.Reader, keyBits)
	if err != nil {
		return nil, err
	}
	now := time.Now()
	template := &x509.Certificate{
		SerialNumber: new(big.Int),
		Subject: pkix.Name{
			CommonName:   fmt.Sprintf("juju-generated CA for environment %q", envName),
			Organization: []string{"juju"},
		},
		NotBefore:             now.UTC().Add(-5 * time.Minute),
		NotAfter:              now.UTC().AddDate(10, 0, 0), // 10 years hence.
		SubjectKeyId:          bigIntHash(priv.N),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:       true,
		MaxPathLen: 0, // Disallow delegation for now.
	}
	certDER, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
	if err != nil {
		return nil, fmt.Errorf("canot create certificate: %v", err)
	}
	var b bytes.Buffer
	pem.Encode(&b, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certDER,
	})
	pem.Encode(&b, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(priv),
	})
	if err := ioutil.WriteFile(path, b.Bytes(), 0600); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}

func generateCert(envName string, caCert *x509.Certificate, caKey *rsa.PrivateKey) ([]byte, error) {
	priv, err := rsa.GenerateKey(rand.Reader, keyBits)
	if err != nil {
		return nil, fmt.Errorf("cannot generate key: %v", err)
	}
	now := time.Now()
	template := &x509.Certificate{
		SerialNumber: new(big.Int),
		Subject: pkix.Name{
			// Note: despite the wildcard, this name will not match
			// host names containing dots.
			CommonName:   "*",
			Organization: []string{"juju"},
		},
		NotBefore: now.UTC().Add(-5 * time.Minute),
		NotAfter:  now.UTC().AddDate(10, 0, 0), // 10 years hence.

		SubjectKeyId: bigIntHash(priv.N),
		KeyUsage:     x509.KeyUsageDataEncipherment,
	}
	certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, &priv.PublicKey, caKey)
	if err != nil {
		return nil, err
	}
	var b bytes.Buffer
	pem.Encode(&b, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certDER,
	})
	pem.Encode(&b, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(priv),
	})
	return b.Bytes(), nil
}

func bigIntHash(n *big.Int) []byte {
	h := sha1.New()
	h.Write(n.Bytes())
	return h.Sum(nil)
}

func parseCAPEM(caPEM []byte, requirePrivateKey bool) (*x509.Certificate, *rsa.PrivateKey, error) {
	var certBlock, keyBlock *pem.Block
	// We split the CA certificate pem into certificate
	// blocks and non-certificate blocks so that
	// it's amenable to checking with tls.X509KeyPair.
	for {
		var b *pem.Block
		b, caPEM = pem.Decode(caPEM)
		if b == nil {
			break
		}
		switch b.Type {
		case "CERTIFICATE":
			if certBlock != nil {
				return nil, nil, fmt.Errorf("more than one certificate found in CA certificate PEM")
			}
			certBlock = b
		case "RSA PRIVATE KEY":
			if keyBlock != nil {
				return nil, nil, fmt.Errorf("more than one key found in CA certificate PEM")
			}
			keyBlock = b
		default:
			log.Printf("juju: unknown PEM block type %q found in CA certificate", b.Type)
		}
	}

	if certBlock == nil {
		return nil, nil, fmt.Errorf("CA PEM holds no certificate")
	}
	cert, err := x509.ParseCertificate(certBlock.Bytes)
	if err != nil {
		return nil, nil, err
	}
	if !cert.BasicConstraintsValid || !cert.IsCA {
		return nil, nil, fmt.Errorf("CA certificate is not a valid CA")
	}
	if keyBlock == nil {
		if requirePrivateKey {
			return nil, nil, fmt.Errorf("CA PEM holds no private key")
		}
		return cert, nil, nil
	}
	tlsCert, err := tls.X509KeyPair(pem.EncodeToMemory(certBlock), pem.EncodeToMemory(keyBlock))
	if err != nil {
		return nil, nil, err
	}
	priv, ok := tlsCert.PrivateKey.(*rsa.PrivateKey)
	if !ok {
		return nil, nil, fmt.Errorf("CA private key has unexpected type %T", tlsCert.PrivateKey)
	}
	return cert, priv, nil
}
<commit_msg>juju: changes to comments for review<commit_after>package juju

import (
	"bytes"
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha1"
	"crypto/tls"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"fmt"
	"io/ioutil"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/log"
	"math/big"
	"os"
	"path/filepath"
	"time"
)

// Bootstrap bootstraps the given environment.  The CA certificate and
// private key in PEM format can be given in caPEM; if this is nil,
// they are read from $HOME/.juju/<environ-name>.pem, or generated and
// written there if the file does not exist.  If uploadTools is true,
// the current version of the juju tools will be uploaded, as documented
// in environs.Environ.Bootstrap.
func Bootstrap(environ environs.Environ, uploadTools bool, caPEM []byte) error {
	if caPEM == nil {
		var err error
		caPEM, err = generateCACert(environ.Name())
		if err != nil {
			return fmt.Errorf("cannot generate CA certificate: %v", err)
		}
	}
	caCert, caKey, err := parseCAPEM(caPEM, true)
	if err != nil {
		return fmt.Errorf("bad CA PEM: %v", err)
	}
	// Generate a new key pair and certificate for
	// the newly bootstrapped instance.
	cert, err := generateCert(environ.Name(), caCert, caKey)
	if err != nil {
		return fmt.Errorf("cannot generate bootstrap certificate: %v", err)
	}
	return environ.Bootstrap(uploadTools, cert)
}

const keyBits = 1024

func generateCACert(envName string) ([]byte, error) {
	path := filepath.Join(os.Getenv("HOME"), ".juju", envName+".pem")
	data, err := ioutil.ReadFile(path)
	if err == nil {
		return data, nil
	} else if !os.IsNotExist(err) {
		return nil, err
	}
	priv, err := rsa.GenerateKey(rand.Reader, keyBits)
	if err != nil {
		return nil, err
	}
	now := time.Now()
	template := &x509.Certificate{
		SerialNumber: new(big.Int),
		Subject: pkix.Name{
			CommonName:   fmt.Sprintf("juju-generated CA for environment %q", envName),
			Organization: []string{"juju"},
		},
		NotBefore:             now.UTC().Add(-5 * time.Minute),
		NotAfter:              now.UTC().AddDate(10, 0, 0), // 10 years hence.
		SubjectKeyId:          bigIntHash(priv.N),
		KeyUsage:              x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		BasicConstraintsValid: true,
		IsCA:       true,
		MaxPathLen: 0, // Disallow delegation for now.
	}
	certDER, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
	if err != nil {
		return nil, fmt.Errorf("canot create certificate: %v", err)
	}
	var b bytes.Buffer
	pem.Encode(&b, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certDER,
	})
	pem.Encode(&b, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(priv),
	})
	if err := ioutil.WriteFile(path, b.Bytes(), 0600); err != nil {
		return nil, err
	}
	return b.Bytes(), nil
}

func generateCert(envName string, caCert *x509.Certificate, caKey *rsa.PrivateKey) ([]byte, error) {
	priv, err := rsa.GenerateKey(rand.Reader, keyBits)
	if err != nil {
		return nil, fmt.Errorf("cannot generate key: %v", err)
	}
	now := time.Now()
	template := &x509.Certificate{
		SerialNumber: new(big.Int),
		Subject: pkix.Name{
			// This won't match host names with dots. The hostname
			// is hardcoded when connecting to avoid the issue.
			CommonName:   "*",
			Organization: []string{"juju"},
		},
		NotBefore: now.UTC().Add(-5 * time.Minute),
		NotAfter:  now.UTC().AddDate(10, 0, 0), // 10 years hence.

		SubjectKeyId: bigIntHash(priv.N),
		KeyUsage:     x509.KeyUsageDataEncipherment,
	}
	certDER, err := x509.CreateCertificate(rand.Reader, template, caCert, &priv.PublicKey, caKey)
	if err != nil {
		return nil, err
	}
	var b bytes.Buffer
	pem.Encode(&b, &pem.Block{
		Type:  "CERTIFICATE",
		Bytes: certDER,
	})
	pem.Encode(&b, &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: x509.MarshalPKCS1PrivateKey(priv),
	})
	return b.Bytes(), nil
}

func bigIntHash(n *big.Int) []byte {
	h := sha1.New()
	h.Write(n.Bytes())
	return h.Sum(nil)
}

func parseCAPEM(caPEM []byte, requirePrivateKey bool) (*x509.Certificate, *rsa.PrivateKey, error) {
	var certBlock, keyBlock *pem.Block
	// We split the CA certificate pem into certificate
	// blocks and non-certificate blocks so that
	// it's amenable to checking with tls.X509KeyPair.
	for {
		var b *pem.Block
		b, caPEM = pem.Decode(caPEM)
		if b == nil {
			break
		}
		switch b.Type {
		case "CERTIFICATE":
			if certBlock != nil {
				return nil, nil, fmt.Errorf("more than one certificate found in CA certificate PEM")
			}
			certBlock = b
		case "RSA PRIVATE KEY":
			if keyBlock != nil {
				return nil, nil, fmt.Errorf("more than one key found in CA certificate PEM")
			}
			keyBlock = b
		default:
			log.Printf("juju: unknown PEM block type %q found in CA certificate", b.Type)
		}
	}

	if certBlock == nil {
		return nil, nil, fmt.Errorf("CA PEM holds no certificate")
	}
	cert, err := x509.ParseCertificate(certBlock.Bytes)
	if err != nil {
		return nil, nil, err
	}
	if !cert.BasicConstraintsValid || !cert.IsCA {
		return nil, nil, fmt.Errorf("CA certificate is not a valid CA")
	}
	if keyBlock == nil {
		if requirePrivateKey {
			return nil, nil, fmt.Errorf("CA PEM holds no private key")
		}
		return cert, nil, nil
	}
	tlsCert, err := tls.X509KeyPair(pem.EncodeToMemory(certBlock), pem.EncodeToMemory(keyBlock))
	if err != nil {
		return nil, nil, err
	}
	priv, ok := tlsCert.PrivateKey.(*rsa.PrivateKey)
	if !ok {
		return nil, nil, fmt.Errorf("CA private key has unexpected type %T", tlsCert.PrivateKey)
	}
	return cert, priv, nil
}
<|endoftext|>


<commit_before>package kace

import (
	"reflect"
	"testing"
)

func TestUnitCamelCase(t *testing.T) {
	var pascalData = []struct {
		i string
		o string
	}{
		{"This is a test", "ThisIsATest"},
		{"this is a test", "ThisIsATest"},
		{"this is 4 test", "ThisIs4Test"},
		{"5this is a test", "5ThisIsATest"},
		{"this_is_a_test", "ThisIsATest"},
		{"This is a test.", "ThisIsATest"},
		{"This.is.a.Test", "ThisIsATest"},
		{"andThisToo", "AndThisToo"},
		{"AndThisToo", "AndThisToo"},
		{"this http conn", "ThisHTTPConn"},
		{"this_https_conn", "ThisHTTPSConn"},
		{"this_http_scan", "ThisHTTPScan"},
		{"willid mess it up", "WillidMessItUp"},
		{"willid_mess_it_up", "WillidMessItUp"},
		{"http_first_upper", "HTTPFirstUpper"},
		{"ahttp_upper", "AhttpUpper"},
	}

	for _, v := range pascalData {
		want := v.o
		got := camelCase(ciTrie, v.i, true)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var camelData = []struct {
		i string
		o string
	}{
		{"this is a test", "thisIsATest"},
		{"this_is_a_test", "thisIsATest"},
		{"this is a test.", "thisIsATest"},
		{"this.is.a.Test", "thisIsATest"},
		{"AndThisToo", "andThisToo"},
		{"andThisToo", "andThisToo"},
		{"this http conn", "thisHTTPConn"},
		{"this_https_conn", "thisHTTPSConn"},
		{"this_http_scan", "thisHTTPScan"},
		{"willid mess it up", "willidMessItUp"},
		{"willid_mess_it_up", "willidMessItUp"},
		{"http_first_lower", "httpFirstLower"},
		{"ahttp_lower", "ahttpLower"},
	}

	for _, v := range camelData {
		want := v.o
		got := camelCase(ciTrie, v.i, false)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestDelimitedCase(t *testing.T) {
	var snakeData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "this_is_a_test"},
		{"ThisIsATest", "this_is_a_test"},
		{"ThisIsATest3", "this_is_a_test3"},
		{"ThisIs44Test", "this_is44_test"},
		{"5ThisIsATest", "5this_is_a_test"},
		{"this is a test", "this_is_a_test"},
		{"this_is_a_test", "this_is_a_test"},
		{"This is a test.", "this_is_a_test"},
		{"This.is.a.Test", "this_is_a_test"},
		{"thisHTTPSConn", "this_https_conn"},
		{"thisHTTPScan", "this_http_scan"},
		{"ThisHTTPSConn", "this_https_conn"},
		{"ThisHTTPScan", "this_http_scan"},
		{"willidMessItUp", "willid_mess_it_up"},
		{"WillidMessItUp", "willid_mess_it_up"},
	}

	for _, v := range snakeData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, snakeDelim, false)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var snakeUpperData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "THIS_IS_A_TEST"},
		{"ThisIsATest", "THIS_IS_A_TEST"},
		{"ThisIsATest3", "THIS_IS_A_TEST3"},
		{"ThisIs44Test", "THIS_IS44_TEST"},
		{"5ThisIsATest", "5THIS_IS_A_TEST"},
		{"this is a test", "THIS_IS_A_TEST"},
		{"this_is_a_test", "THIS_IS_A_TEST"},
		{"This is a test.", "THIS_IS_A_TEST"},
		{"This.is.a.Test", "THIS_IS_A_TEST"},
		{"thisHTTPSConn", "THIS_HTTPS_CONN"},
		{"ThisHTTPSConn", "THIS_HTTPS_CONN"},
		{"willidMessItUp", "WILLID_MESS_IT_UP"},
		{"WillidMessItUp", "WILLID_MESS_IT_UP"},
	}

	for _, v := range snakeUpperData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, snakeDelim, true)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var kebabData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "this-is-a-test"},
		{"ThisIsATest", "this-is-a-test"},
		{"ThisIsATest3", "this-is-a-test3"},
		{"ThisIs44Test", "this-is44-test"},
		{"5ThisIsATest", "5this-is-a-test"},
		{"this is a test", "this-is-a-test"},
		{"this_is_a_test", "this-is-a-test"},
		{"This is a test.", "this-is-a-test"},
		{"This.is.a.Test", "this-is-a-test"},
		{"thisHTTPSConn", "this-https-conn"},
		{"ThisHTTPSConn", "this-https-conn"},
		{"willidMessItUp", "willid-mess-it-up"},
		{"WillidMessItUp", "willid-mess-it-up"},
	}

	for _, v := range kebabData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, kebabDelim, false)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var kebabUpperData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "THIS-IS-A-TEST"},
		{"ThisIsATest", "THIS-IS-A-TEST"},
		{"ThisIsATest3", "THIS-IS-A-TEST3"},
		{"ThisIs44Test", "THIS-IS44-TEST"},
		{"5ThisIsATest", "5THIS-IS-A-TEST"},
		{"this is a test", "THIS-IS-A-TEST"},
		{"this_is_a_test", "THIS-IS-A-TEST"},
		{"This is a test.", "THIS-IS-A-TEST"},
		{"This.is.a.Test", "THIS-IS-A-TEST"},
		{"thisHTTPSConn", "THIS-HTTPS-CONN"},
		{"ThisHTTPSConn", "THIS-HTTPS-CONN"},
		{"willidMessItUp", "WILLID-MESS-IT-UP"},
		{"WillidMessItUp", "WILLID-MESS-IT-UP"},
	}

	for _, v := range kebabUpperData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, kebabDelim, true)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestUnitAppendCased(t *testing.T) {
	data := []struct {
		in  []rune
		up  bool
		apd rune
		out string
	}{
		{[]rune("tes"), true, 't', "tesT"},
		{[]rune("te541s"), false, 't', "te541st"},
	}

	for _, v := range data {
		want := v.out
		got := string(appendCased(v.in, v.up, v.apd))
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestUnitReverse(t *testing.T) {
	data := []struct {
		bef []rune
		aft string
	}{
		{[]rune("test"), "tset"},
		{[]rune("te541st"), "ts145et"},
	}

	for _, v := range data {
		want := v.aft
		reverse(v.bef)
		got := string(v.bef)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestRegularizeCI(t *testing.T) {
	data := []struct {
		in  map[string]bool
		out map[string]bool
	}{
		{
			map[string]bool{
				"nsa": true,
				"CIA": false,
				"fbI": true,
			},
			map[string]bool{
				"NSA": true,
				"CIA": true,
				"FBI": true,
			},
		},
		{ciMap, ciMap},
	}

	for _, v := range data {
		want := v.out
		got := regularizeCI(v.in)
		if !reflect.DeepEqual(got, want) {
			t.Errorf("got %v, want %v", got, want)
		}
	}

}
<commit_msg>Fix name of reverse unit test func.<commit_after>package kace

import (
	"reflect"
	"testing"
)

func TestUnitCamelCase(t *testing.T) {
	var pascalData = []struct {
		i string
		o string
	}{
		{"This is a test", "ThisIsATest"},
		{"this is a test", "ThisIsATest"},
		{"this is 4 test", "ThisIs4Test"},
		{"5this is a test", "5ThisIsATest"},
		{"this_is_a_test", "ThisIsATest"},
		{"This is a test.", "ThisIsATest"},
		{"This.is.a.Test", "ThisIsATest"},
		{"andThisToo", "AndThisToo"},
		{"AndThisToo", "AndThisToo"},
		{"this http conn", "ThisHTTPConn"},
		{"this_https_conn", "ThisHTTPSConn"},
		{"this_http_scan", "ThisHTTPScan"},
		{"willid mess it up", "WillidMessItUp"},
		{"willid_mess_it_up", "WillidMessItUp"},
		{"http_first_upper", "HTTPFirstUpper"},
		{"ahttp_upper", "AhttpUpper"},
	}

	for _, v := range pascalData {
		want := v.o
		got := camelCase(ciTrie, v.i, true)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var camelData = []struct {
		i string
		o string
	}{
		{"this is a test", "thisIsATest"},
		{"this_is_a_test", "thisIsATest"},
		{"this is a test.", "thisIsATest"},
		{"this.is.a.Test", "thisIsATest"},
		{"AndThisToo", "andThisToo"},
		{"andThisToo", "andThisToo"},
		{"this http conn", "thisHTTPConn"},
		{"this_https_conn", "thisHTTPSConn"},
		{"this_http_scan", "thisHTTPScan"},
		{"willid mess it up", "willidMessItUp"},
		{"willid_mess_it_up", "willidMessItUp"},
		{"http_first_lower", "httpFirstLower"},
		{"ahttp_lower", "ahttpLower"},
	}

	for _, v := range camelData {
		want := v.o
		got := camelCase(ciTrie, v.i, false)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestDelimitedCase(t *testing.T) {
	var snakeData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "this_is_a_test"},
		{"ThisIsATest", "this_is_a_test"},
		{"ThisIsATest3", "this_is_a_test3"},
		{"ThisIs44Test", "this_is44_test"},
		{"5ThisIsATest", "5this_is_a_test"},
		{"this is a test", "this_is_a_test"},
		{"this_is_a_test", "this_is_a_test"},
		{"This is a test.", "this_is_a_test"},
		{"This.is.a.Test", "this_is_a_test"},
		{"thisHTTPSConn", "this_https_conn"},
		{"thisHTTPScan", "this_http_scan"},
		{"ThisHTTPSConn", "this_https_conn"},
		{"ThisHTTPScan", "this_http_scan"},
		{"willidMessItUp", "willid_mess_it_up"},
		{"WillidMessItUp", "willid_mess_it_up"},
	}

	for _, v := range snakeData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, snakeDelim, false)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var snakeUpperData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "THIS_IS_A_TEST"},
		{"ThisIsATest", "THIS_IS_A_TEST"},
		{"ThisIsATest3", "THIS_IS_A_TEST3"},
		{"ThisIs44Test", "THIS_IS44_TEST"},
		{"5ThisIsATest", "5THIS_IS_A_TEST"},
		{"this is a test", "THIS_IS_A_TEST"},
		{"this_is_a_test", "THIS_IS_A_TEST"},
		{"This is a test.", "THIS_IS_A_TEST"},
		{"This.is.a.Test", "THIS_IS_A_TEST"},
		{"thisHTTPSConn", "THIS_HTTPS_CONN"},
		{"ThisHTTPSConn", "THIS_HTTPS_CONN"},
		{"willidMessItUp", "WILLID_MESS_IT_UP"},
		{"WillidMessItUp", "WILLID_MESS_IT_UP"},
	}

	for _, v := range snakeUpperData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, snakeDelim, true)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var kebabData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "this-is-a-test"},
		{"ThisIsATest", "this-is-a-test"},
		{"ThisIsATest3", "this-is-a-test3"},
		{"ThisIs44Test", "this-is44-test"},
		{"5ThisIsATest", "5this-is-a-test"},
		{"this is a test", "this-is-a-test"},
		{"this_is_a_test", "this-is-a-test"},
		{"This is a test.", "this-is-a-test"},
		{"This.is.a.Test", "this-is-a-test"},
		{"thisHTTPSConn", "this-https-conn"},
		{"ThisHTTPSConn", "this-https-conn"},
		{"willidMessItUp", "willid-mess-it-up"},
		{"WillidMessItUp", "willid-mess-it-up"},
	}

	for _, v := range kebabData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, kebabDelim, false)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}

	var kebabUpperData = []struct {
		i string
		o string
	}{
		{"thisIsATest", "THIS-IS-A-TEST"},
		{"ThisIsATest", "THIS-IS-A-TEST"},
		{"ThisIsATest3", "THIS-IS-A-TEST3"},
		{"ThisIs44Test", "THIS-IS44-TEST"},
		{"5ThisIsATest", "5THIS-IS-A-TEST"},
		{"this is a test", "THIS-IS-A-TEST"},
		{"this_is_a_test", "THIS-IS-A-TEST"},
		{"This is a test.", "THIS-IS-A-TEST"},
		{"This.is.a.Test", "THIS-IS-A-TEST"},
		{"thisHTTPSConn", "THIS-HTTPS-CONN"},
		{"ThisHTTPSConn", "THIS-HTTPS-CONN"},
		{"willidMessItUp", "WILLID-MESS-IT-UP"},
		{"WillidMessItUp", "WILLID-MESS-IT-UP"},
	}

	for _, v := range kebabUpperData {
		want := v.o
		got := delimitedCase(ciTrie, v.i, kebabDelim, true)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestUnitAppendCased(t *testing.T) {
	data := []struct {
		in  []rune
		up  bool
		apd rune
		out string
	}{
		{[]rune("tes"), true, 't', "tesT"},
		{[]rune("te541s"), false, 't', "te541st"},
	}

	for _, v := range data {
		want := v.out
		got := string(appendCased(v.in, v.up, v.apd))
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestUnitReverse(t *testing.T) {
	data := []struct {
		bef []rune
		aft string
	}{
		{[]rune("test"), "tset"},
		{[]rune("te541st"), "ts145et"},
	}

	for _, v := range data {
		want := v.aft
		reverse(v.bef)
		got := string(v.bef)
		if got != want {
			t.Errorf("got %v, want %v", got, want)
		}
	}
}

func TestUnitRegularizeCI(t *testing.T) {
	data := []struct {
		in  map[string]bool
		out map[string]bool
	}{
		{
			map[string]bool{
				"nsa": true,
				"CIA": false,
				"fbI": true,
			},
			map[string]bool{
				"NSA": true,
				"CIA": true,
				"FBI": true,
			},
		},
		{ciMap, ciMap},
	}

	for _, v := range data {
		want := v.out
		got := regularizeCI(v.in)
		if !reflect.DeepEqual(got, want) {
			t.Errorf("got %v, want %v", got, want)
		}
	}

}
<|endoftext|>


<commit_before>package oval

import (
	"encoding/xml"
)

// Root : root object
type Root struct {
	XMLName     xml.Name    `xml:"oval_definitions"`
	Generator   Generator   `xml:"generator"`
	Definitions Definitions `xml:"definitions"`
	Tests       Tests       `xml:"tests"`
	Objects     Objects     `xml:"objects"`
	States      States      `xml:"states"`
}

// Generator : >generator
type Generator struct {
	XMLName        xml.Name `xml:"generator"`
	ProductName    string   `xml:"product_name"`
	ProductVersion string   `xml:"product_version"`
	SchemaVersion  string   `xml:"schema_version"`
	Timestamp      string   `xml:"timestamp"`
}

// Definitions : >definitions
type Definitions struct {
	XMLName     xml.Name     `xml:"definitions"`
	Definitions []Definition `xml:"definition"`
}

// Definition : >definitions>definition
type Definition struct {
	XMLName     xml.Name    `xml:"definition"`
	ID          string      `xml:"id,attr"`
	Class       string      `xml:"class,attr"`
	Title       string      `xml:"metadata>title"`
	Affecteds   []Affected  `xml:"metadata>affected"`
	References  []Reference `xml:"metadata>reference"`
	Description string      `xml:"metadata>description"`
	Advisory    Advisory    `xml:"metadata>advisory"` // RedHat, Oracle, Ubuntu
	Debian      Debian      `xml:"metadata>debian"`   // Debian
	Criteria    Criteria    `xml:"criteria"`
}

// Criteria : >definitions>definition>criteria
type Criteria struct {
	XMLName    xml.Name    `xml:"criteria"`
	Operator   string      `xml:"operator,attr"`
	Criterias  []Criteria  `xml:"criteria"`
	Criterions []Criterion `xml:"criterion"`
}

// Criterion : >definitions>definition>criteria>*>criterion
type Criterion struct {
	XMLName xml.Name `xml:"criterion"`
	Negate  bool     `xml:"negate,attr"`
	TestRef string   `xml:"test_ref,attr"`
	Comment string   `xml:"comment,attr"`
}

// Affected : >definitions>definition>metadata>affected
type Affected struct {
	XMLName   xml.Name `xml:"affected"`
	Family    string   `xml:"family,attr"`
	Platforms []string `xml:"platform"`
}

// Reference : >definitions>definition>metadata>reference
type Reference struct {
	XMLName xml.Name `xml:"reference"`
	Source  string   `xml:"source,attr"`
	RefID   string   `xml:"ref_id,attr"`
	RefURL  string   `xml:"ref_url,attr"`
}

// Advisory : >definitions>definition>metadata>advisory
// RedHat and Ubuntu OVAL
type Advisory struct {
	XMLName         xml.Name   `xml:"advisory"`
	Severity        string     `xml:"severity"`
	Cves            []Cve      `xml:"cve"`
	Bugzillas       []Bugzilla `xml:"bugzilla"`
	AffectedCPEList []string   `xml:"affected_cpe_list>cpe"`
	Refs            []Ref      `xml:"ref"` // Ubuntu Only
	Bugs            []Bug      `xml:"bug"` // Ubuntu Only
	Issued          struct {
		Date string `xml:"date,attr"`
	} `xml:"issued"`
	Updated struct {
		Date string `xml:"date,attr"`
	} `xml:"updated"`
}

// Ref : >definitions>definition>metadata>advisory>ref
// Ubuntu OVAL
type Ref struct {
	XMLName xml.Name `xml:"ref"`
	URL     string   `xml:",chardata"`
}

// Bug : >definitions>definition>metadata>advisory>bug
// Ubuntu OVAL
type Bug struct {
	XMLName xml.Name `xml:"bug"`
	URL     string   `xml:",chardata"`
}

// Cve : >definitions>definition>metadata>advisory>cve
// RedHat OVAL
type Cve struct {
	XMLName xml.Name `xml:"cve"`
	CveID   string   `xml:",chardata"`
	Cvss2   string   `xml:"cvss2,attr"`
	Cvss3   string   `xml:"cvss3,attr"`
	Cwe     string   `xml:"cwe,attr"`
	Href    string   `xml:"href,attr"`
	Public  string   `xml:"public,attr"`
}

// Bugzilla : >definitions>definition>metadata>advisory>bugzilla
// RedHat OVAL
type Bugzilla struct {
	XMLName xml.Name `xml:"bugzilla"`
	ID      string   `xml:"id,attr"`
	URL     string   `xml:"href,attr"`
	Title   string   `xml:",chardata"`
}

// Debian : >definitions>definition>metadata>debian
type Debian struct {
	XMLName  xml.Name `xml:"debian"`
	MoreInfo string   `xml:"moreinfo"`
	Date     string   `xml:"date"`
}

// Tests : >tests
type Tests struct {
	XMLName        xml.Name        `xml:"tests"`
	LineTests      []LineTest      `xml:"line_test"`
	Version55Tests []Version55Test `xml:"version55_test"`
}

// LineTest : >tests>line_test
type LineTest struct {
	XMLName       xml.Name    `xml:"line_test"`
	ID            string      `xml:"id,attr"`
	StateOperator string      `xml:"state_operator,attr"`
	ObjectRefs    []ObjectRef `xml:"object"`
	StateRefs     []StateRef  `xml:"state"`
	Comment       string      `xml:"comment,attr"`
}

// Version55Test : >tests>version55_test
type Version55Test struct {
	XMLName       xml.Name    `xml:"version55_test"`
	ID            string      `xml:"id,attr"`
	StateOperator string      `xml:"state_operator,attr"`
	ObjectRefs    []ObjectRef `xml:"object"`
	StateRefs     []StateRef  `xml:"state"`
	Comment       string      `xml:"comment,attr"`
}

// ObjectRef : >tests>line_test>object-object_ref
//           : >tests>version55_test>object-object_ref
type ObjectRef struct {
	XMLName   xml.Name `xml:"object"`
	ObjectRef string   `xml:"object_ref,attr"`
}

// StateRef : >tests>line_test>state-state_ref
//          : >tests>version55_test>state-state_ref
type StateRef struct {
	XMLName  xml.Name `xml:"state"`
	StateRef string   `xml:"state_ref,attr"`
}

// Objects : >objects
type Objects struct {
	XMLName          xml.Name          `xml:"objects"`
	LineObjects      []LineObject      `xml:"line_object"`
	Version55Objects []Version55Object `xml:"version55_object"`
}

// LineObject : >objects>line_object
type LineObject struct {
	XMLName         xml.Name `xml:"line_object"`
	ID              string   `xml:"id,attr"`
	ShowSubcommands []string `xml:"show_subcommand"`
}

// Version55Object : >objects>version55_object
type Version55Object struct {
	XMLName xml.Name `xml:"version55_object"`
	ID      string   `xml:"id,attr"`
}

// States : >states
type States struct {
	XMLName         xml.Name         `xml:"states"`
	LineStates      []LineState      `xml:"line_state"`
	Version55States []Version55State `xml:"version55_state"`
}

// LineState : >states>line_state
type LineState struct {
	XMLName        xml.Name   `xml:"line_state"`
	ID             string     `xml:"id,attr"`
	ShowSubcommand string     `xml:"show_subcommand"`
	ConfigLine     ConfigLine `xml:"config_line"`
}

// ConfigLine : >states>line_state>config_line
type ConfigLine struct {
	XMLName   xml.Name `xml:"config_line"`
	Body      string   `xml:",chardata"`
	Operation string   `xml:"operation,attr"`
}

// Version55State : >states>version55_state
type Version55State struct {
	XMLName       xml.Name `xml:"version55_state"`
	ID            string   `xml:"id,attr"`
	VersionString string   `xml:"version_string"`
}
<commit_msg>Add CVE>Impact<commit_after>package oval

import (
	"encoding/xml"
)

// Root : root object
type Root struct {
	XMLName     xml.Name    `xml:"oval_definitions"`
	Generator   Generator   `xml:"generator"`
	Definitions Definitions `xml:"definitions"`
	Tests       Tests       `xml:"tests"`
	Objects     Objects     `xml:"objects"`
	States      States      `xml:"states"`
}

// Generator : >generator
type Generator struct {
	XMLName        xml.Name `xml:"generator"`
	ProductName    string   `xml:"product_name"`
	ProductVersion string   `xml:"product_version"`
	SchemaVersion  string   `xml:"schema_version"`
	Timestamp      string   `xml:"timestamp"`
}

// Definitions : >definitions
type Definitions struct {
	XMLName     xml.Name     `xml:"definitions"`
	Definitions []Definition `xml:"definition"`
}

// Definition : >definitions>definition
type Definition struct {
	XMLName     xml.Name    `xml:"definition"`
	ID          string      `xml:"id,attr"`
	Class       string      `xml:"class,attr"`
	Title       string      `xml:"metadata>title"`
	Affecteds   []Affected  `xml:"metadata>affected"`
	References  []Reference `xml:"metadata>reference"`
	Description string      `xml:"metadata>description"`
	Advisory    Advisory    `xml:"metadata>advisory"` // RedHat, Oracle, Ubuntu
	Debian      Debian      `xml:"metadata>debian"`   // Debian
	Criteria    Criteria    `xml:"criteria"`
}

// Criteria : >definitions>definition>criteria
type Criteria struct {
	XMLName    xml.Name    `xml:"criteria"`
	Operator   string      `xml:"operator,attr"`
	Criterias  []Criteria  `xml:"criteria"`
	Criterions []Criterion `xml:"criterion"`
}

// Criterion : >definitions>definition>criteria>*>criterion
type Criterion struct {
	XMLName xml.Name `xml:"criterion"`
	Negate  bool     `xml:"negate,attr"`
	TestRef string   `xml:"test_ref,attr"`
	Comment string   `xml:"comment,attr"`
}

// Affected : >definitions>definition>metadata>affected
type Affected struct {
	XMLName   xml.Name `xml:"affected"`
	Family    string   `xml:"family,attr"`
	Platforms []string `xml:"platform"`
}

// Reference : >definitions>definition>metadata>reference
type Reference struct {
	XMLName xml.Name `xml:"reference"`
	Source  string   `xml:"source,attr"`
	RefID   string   `xml:"ref_id,attr"`
	RefURL  string   `xml:"ref_url,attr"`
}

// Advisory : >definitions>definition>metadata>advisory
// RedHat and Ubuntu OVAL
type Advisory struct {
	XMLName         xml.Name   `xml:"advisory"`
	Severity        string     `xml:"severity"`
	Cves            []Cve      `xml:"cve"`
	Bugzillas       []Bugzilla `xml:"bugzilla"`
	AffectedCPEList []string   `xml:"affected_cpe_list>cpe"`
	Refs            []Ref      `xml:"ref"` // Ubuntu Only
	Bugs            []Bug      `xml:"bug"` // Ubuntu Only
	Issued          struct {
		Date string `xml:"date,attr"`
	} `xml:"issued"`
	Updated struct {
		Date string `xml:"date,attr"`
	} `xml:"updated"`
}

// Ref : >definitions>definition>metadata>advisory>ref
// Ubuntu OVAL
type Ref struct {
	XMLName xml.Name `xml:"ref"`
	URL     string   `xml:",chardata"`
}

// Bug : >definitions>definition>metadata>advisory>bug
// Ubuntu OVAL
type Bug struct {
	XMLName xml.Name `xml:"bug"`
	URL     string   `xml:",chardata"`
}

// Cve : >definitions>definition>metadata>advisory>cve
// RedHat OVAL
type Cve struct {
	XMLName xml.Name `xml:"cve"`
	CveID   string   `xml:",chardata"`
	Cvss2   string   `xml:"cvss2,attr"`
	Cvss3   string   `xml:"cvss3,attr"`
	Cwe     string   `xml:"cwe,attr"`
	Impact  string   `xml:"impact,attr"`
	Href    string   `xml:"href,attr"`
	Public  string   `xml:"public,attr"`
}

// Bugzilla : >definitions>definition>metadata>advisory>bugzilla
// RedHat OVAL
type Bugzilla struct {
	XMLName xml.Name `xml:"bugzilla"`
	ID      string   `xml:"id,attr"`
	URL     string   `xml:"href,attr"`
	Title   string   `xml:",chardata"`
}

// Debian : >definitions>definition>metadata>debian
type Debian struct {
	XMLName  xml.Name `xml:"debian"`
	MoreInfo string   `xml:"moreinfo"`
	Date     string   `xml:"date"`
}

// Tests : >tests
type Tests struct {
	XMLName        xml.Name        `xml:"tests"`
	LineTests      []LineTest      `xml:"line_test"`
	Version55Tests []Version55Test `xml:"version55_test"`
}

// LineTest : >tests>line_test
type LineTest struct {
	XMLName       xml.Name    `xml:"line_test"`
	ID            string      `xml:"id,attr"`
	StateOperator string      `xml:"state_operator,attr"`
	ObjectRefs    []ObjectRef `xml:"object"`
	StateRefs     []StateRef  `xml:"state"`
	Comment       string      `xml:"comment,attr"`
}

// Version55Test : >tests>version55_test
type Version55Test struct {
	XMLName       xml.Name    `xml:"version55_test"`
	ID            string      `xml:"id,attr"`
	StateOperator string      `xml:"state_operator,attr"`
	ObjectRefs    []ObjectRef `xml:"object"`
	StateRefs     []StateRef  `xml:"state"`
	Comment       string      `xml:"comment,attr"`
}

// ObjectRef : >tests>line_test>object-object_ref
//           : >tests>version55_test>object-object_ref
type ObjectRef struct {
	XMLName   xml.Name `xml:"object"`
	ObjectRef string   `xml:"object_ref,attr"`
}

// StateRef : >tests>line_test>state-state_ref
//          : >tests>version55_test>state-state_ref
type StateRef struct {
	XMLName  xml.Name `xml:"state"`
	StateRef string   `xml:"state_ref,attr"`
}

// Objects : >objects
type Objects struct {
	XMLName          xml.Name          `xml:"objects"`
	LineObjects      []LineObject      `xml:"line_object"`
	Version55Objects []Version55Object `xml:"version55_object"`
}

// LineObject : >objects>line_object
type LineObject struct {
	XMLName         xml.Name `xml:"line_object"`
	ID              string   `xml:"id,attr"`
	ShowSubcommands []string `xml:"show_subcommand"`
}

// Version55Object : >objects>version55_object
type Version55Object struct {
	XMLName xml.Name `xml:"version55_object"`
	ID      string   `xml:"id,attr"`
}

// States : >states
type States struct {
	XMLName         xml.Name         `xml:"states"`
	LineStates      []LineState      `xml:"line_state"`
	Version55States []Version55State `xml:"version55_state"`
}

// LineState : >states>line_state
type LineState struct {
	XMLName        xml.Name   `xml:"line_state"`
	ID             string     `xml:"id,attr"`
	ShowSubcommand string     `xml:"show_subcommand"`
	ConfigLine     ConfigLine `xml:"config_line"`
}

// ConfigLine : >states>line_state>config_line
type ConfigLine struct {
	XMLName   xml.Name `xml:"config_line"`
	Body      string   `xml:",chardata"`
	Operation string   `xml:"operation,attr"`
}

// Version55State : >states>version55_state
type Version55State struct {
	XMLName       xml.Name `xml:"version55_state"`
	ID            string   `xml:"id,attr"`
	VersionString string   `xml:"version_string"`
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Collins
//
// 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 steven

import (
	"math"

	"github.com/go-gl/mathgl/mgl32"
	"github.com/thinkofdeath/steven/type/direction"
	"github.com/thinkofdeath/steven/ui"
)

func modelToUI(mdl *model) *ui.Model {
	mat := mgl32.Rotate3DX(math.Pi / 6).Mat4().
		Mul4(mgl32.Rotate3DY(math.Pi / 4).Mat4())
	var verts []*ui.ModelVertex
	for i := range mdl.elements {
		e := mdl.elements[len(mdl.elements)-1-i]
		for fi, f := range e.faces {
			if f == nil {
				continue
			}

			face := faceVertices[fi]

			x, w := e.from[0]/16, (e.to[0]-e.from[0])/16
			y, h := e.from[1]/16, (e.to[1]-e.from[1])/16
			z, d := e.from[2]/16, (e.to[2]-e.from[2])/16
			tex := mdl.lookupTexture(f.texture)
			for _, v := range face.verts {
				var rr, gg, bb byte = 255, 255, 255
				if direction.Type(fi) == direction.West || direction.Type(fi) == direction.East {
					rr = byte(255 * 0.8)
					gg = byte(255 * 0.8)
					bb = byte(255 * 0.8)
				}

				vert := &ui.ModelVertex{
					X:        float32(float64(v.X)*w + x),
					Y:        float32(float64(v.Y)*h + y),
					Z:        float32(float64(v.Z)*d + z),
					TOffsetX: int16(float64(v.TOffsetX*16*int16(tex.Width)) * 1),
					TOffsetY: int16(float64(v.TOffsetY*16*int16(tex.Height)) * 1),
					R:        rr,
					G:        gg,
					B:        bb,
					A:        255,
					TX:       uint16(tex.X),
					TY:       uint16(tex.Y),
					TW:       uint16(tex.Width),
					TH:       uint16(tex.Height),
					TAtlas:   int16(tex.Atlas),
				}
				verts = append(verts, vert)
			}
		}
	}
	return ui.NewModel(0, 0, verts, mat)
}
<commit_msg>steven: change the lighting direction on ui models<commit_after>// Copyright 2015 Matthew Collins
//
// 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 steven

import (
	"math"

	"github.com/go-gl/mathgl/mgl32"
	"github.com/thinkofdeath/steven/type/direction"
	"github.com/thinkofdeath/steven/ui"
)

func modelToUI(mdl *model) *ui.Model {
	mat := mgl32.Rotate3DX(math.Pi / 6).Mat4().
		Mul4(mgl32.Rotate3DY(math.Pi / 4).Mat4())
	var verts []*ui.ModelVertex
	for i := range mdl.elements {
		e := mdl.elements[len(mdl.elements)-1-i]
		for fi, f := range e.faces {
			if f == nil {
				continue
			}

			face := faceVertices[fi]

			x, w := e.from[0]/16, (e.to[0]-e.from[0])/16
			y, h := e.from[1]/16, (e.to[1]-e.from[1])/16
			z, d := e.from[2]/16, (e.to[2]-e.from[2])/16
			tex := mdl.lookupTexture(f.texture)
			for _, v := range face.verts {
				var rr, gg, bb byte = 255, 255, 255
				if direction.Type(fi) == direction.North || direction.Type(fi) == direction.South {
					rr = byte(255 * 0.8)
					gg = byte(255 * 0.8)
					bb = byte(255 * 0.8)
				}

				vert := &ui.ModelVertex{
					X:        float32(float64(v.X)*w + x),
					Y:        float32(float64(v.Y)*h + y),
					Z:        float32(float64(v.Z)*d + z),
					TOffsetX: int16(float64(v.TOffsetX*16*int16(tex.Width)) * 1),
					TOffsetY: int16(float64(v.TOffsetY*16*int16(tex.Height)) * 1),
					R:        rr,
					G:        gg,
					B:        bb,
					A:        255,
					TX:       uint16(tex.X),
					TY:       uint16(tex.Y),
					TW:       uint16(tex.Width),
					TH:       uint16(tex.Height),
					TAtlas:   int16(tex.Atlas),
				}
				verts = append(verts, vert)
			}
		}
	}
	return ui.NewModel(0, 0, verts, mat)
}
<|endoftext|>


<commit_before>package clone

import (
	"crypto/md5"
	"encoding/hex"
	"github.com/jeffail/tunny"
	csv "github.com/whosonfirst/go-whosonfirst-csv"
	log "github.com/whosonfirst/go-whosonfirst-log"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
)

type WOFClone struct {
	Source    string
	Dest      string
	Count     int64
	Success   int64
	Error     int64
	Skipped   int64
	Scheduled int64
	Completed int64
	Logger    *log.WOFLogger
	client    *http.Client
	pool      *tunny.WorkPool
}

func NewWOFClone(source string, dest string, procs int, logger *log.WOFLogger) *WOFClone {

	// cd := &sync.Cond{L: &sync.Mutex{}}

	cl := &http.Client{}

	runtime.GOMAXPROCS(procs)

	pool, _ := tunny.CreatePoolGeneric(procs).Open()

	c := WOFClone{
		Count:   0,
		Success: 0,
		Error:   0,
		Skipped: 0,
		Source:  source,
		Dest:    dest,
		Logger:  logger,
		client:  cl,
		pool:    pool,
	}

	return &c
}

func (c *WOFClone) CloneMetaFile(file string) error {

	abs_path, _ := filepath.Abs(file)
	// c.Logger.Debug("Parse meta file %s", abs_path)

	reader, read_err := csv.NewDictReader(abs_path)

	if read_err != nil {
		c.Logger.Error("Failed to read %s, because %v", abs_path, read_err)
		return read_err
	}

	wg := new(sync.WaitGroup)

	for {
		row, err := reader.Read()

		if err == io.EOF {
			break
		}

		if err != nil {
			return err
		}

		rel_path, ok := row["path"]

		if !ok {
			continue
		}

		// this does not account for counts - need to think about who calls what, when
		// probably needs to moved in to ClonePath... (20151105/thisisaaronland)

		ensure_changes := true
		skip_existing := false

		remote := c.Source + rel_path
		local := path.Join(c.Dest, rel_path)

		if !os.IsNotExist(err) {

			if skip_existing {
				c.Logger.Debug("%s already exists and we are skipping things that exist", local)
				continue
			}

			change, _ := c.HasChanged(local, remote)

			if !change {
				c.Logger.Info("no changes to %s", local)
				continue
			}
		}

		wg.Add(1)
		atomic.AddInt64(&c.Scheduled, 1)

		go func() {

			defer wg.Done()

			_, err = c.pool.SendWork(func() {

				cl_err := c.ClonePath(rel_path, ensure_changes)

				if cl_err != nil {
					atomic.AddInt64(&c.Error, 1)
				} else {
					atomic.AddInt64(&c.Success, 1)
				}

				atomic.AddInt64(&c.Completed, 1)
				c.Status()
			})

		}()
	}

	wg.Wait()
	return nil
}

func (c *WOFClone) ClonePath(rel_path string, ensure_changes bool) error {

	atomic.AddInt64(&c.Count, 1)

	remote := c.Source + rel_path
	local := path.Join(c.Dest, rel_path)

	_, err := os.Stat(local)

	if !os.IsNotExist(err) && ensure_changes {

		change, _ := c.HasChanged(local, remote)

		if !change {

			c.Logger.Debug("%s has not changed so skipping", local)
			atomic.AddInt64(&c.Skipped, 1)
			return nil
		}

	}

	process_err := c.Process(remote, local)

	if process_err != nil {
		atomic.AddInt64(&c.Error, 1)
		return process_err
	}

	return nil
}

// don't return true if there's a problem - move that logic up above

func (c *WOFClone) HasChanged(local string, remote string) (bool, error) {

	change := true

	body, err := ioutil.ReadFile(local)

	if err != nil {
		c.Logger.Error("Failed to read %s, becase %v", local, err)
		return change, err
	}

	hash := md5.Sum(body)
	local_hash := hex.EncodeToString(hash[:])

	return c.HasHashChanged(local_hash, remote)
}

func (c *WOFClone) HasHashChanged(local_hash string, remote string) (bool, error) {

	change := true

	rsp, err := c.Fetch("HEAD", remote)

	if err != nil {
		return change, err
	}

	defer rsp.Body.Close()

	etag := rsp.Header.Get("Etag")
	remote_hash := strings.Replace(etag, "\"", "", -1)

	if local_hash == remote_hash {
		change = false
	}

	return change, nil
}

func (c *WOFClone) Process(remote string, local string) error {

	c.Logger.Debug("fetch %s and store in %s", remote, local)

	local_root := path.Dir(local)

	_, err := os.Stat(local_root)

	if os.IsNotExist(err) {
		c.Logger.Info("create %s", local_root)
		os.MkdirAll(local_root, 0755)
	}

	rsp, fetch_err := c.Fetch("GET", remote)

	if fetch_err != nil {
		return fetch_err
	}

	defer rsp.Body.Close()

	contents, read_err := ioutil.ReadAll(rsp.Body)

	if read_err != nil {
		c.Logger.Error("failed to read body for %s, because %v", remote, read_err)
		return read_err
	}

	go func() error {

		write_err := ioutil.WriteFile(local, contents, 0644)

		if write_err != nil {
			c.Logger.Error("Failed to write %s, because %v", local, write_err)

			atomic.AddInt64(&c.Success, -1)
			atomic.AddInt64(&c.Error, 1)

			return write_err
		}

		c.Logger.Debug("Wrote %s to disk", local)
		return nil
	}()

	return nil
}

func (c *WOFClone) Fetch(method string, url string) (*http.Response, error) {

	c.Logger.Debug("%s %s", method, url)

	req, _ := http.NewRequest(method, url, nil)
	req.Close = true

	rsp, err := c.client.Do(req)

	if err != nil {
		c.Logger.Error("Failed to %s %s, because %v", method, url, err)
		// golog.Fatal(err)
		return nil, err
	}

	return rsp, err
}

func (c *WOFClone) Status() {
	c.Logger.Info("scheduled: %d completed: %d success: %d error: %d goroutines: %d", c.Scheduled, c.Completed, c.Success, c.Error, runtime.NumGoroutine())
}
<commit_msg>actually check HTTP status code per issue #6<commit_after>package clone

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"github.com/jeffail/tunny"
	csv "github.com/whosonfirst/go-whosonfirst-csv"
	log "github.com/whosonfirst/go-whosonfirst-log"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"sync/atomic"
)

type WOFClone struct {
	Source    string
	Dest      string
	Count     int64
	Success   int64
	Error     int64
	Skipped   int64
	Scheduled int64
	Completed int64
	Logger    *log.WOFLogger
	client    *http.Client
	pool      *tunny.WorkPool
}

func NewWOFClone(source string, dest string, procs int, logger *log.WOFLogger) *WOFClone {

	// cd := &sync.Cond{L: &sync.Mutex{}}

	cl := &http.Client{}

	runtime.GOMAXPROCS(procs)

	pool, _ := tunny.CreatePoolGeneric(procs).Open()

	c := WOFClone{
		Count:   0,
		Success: 0,
		Error:   0,
		Skipped: 0,
		Source:  source,
		Dest:    dest,
		Logger:  logger,
		client:  cl,
		pool:    pool,
	}

	return &c
}

func (c *WOFClone) CloneMetaFile(file string) error {

	abs_path, _ := filepath.Abs(file)
	// c.Logger.Debug("Parse meta file %s", abs_path)

	reader, read_err := csv.NewDictReader(abs_path)

	if read_err != nil {
		c.Logger.Error("Failed to read %s, because %v", abs_path, read_err)
		return read_err
	}

	wg := new(sync.WaitGroup)

	for {
		row, err := reader.Read()

		if err == io.EOF {
			break
		}

		if err != nil {
			return err
		}

		rel_path, ok := row["path"]

		if !ok {
			continue
		}

		// this does not account for counts - need to think about who calls what, when
		// probably needs to moved in to ClonePath... (20151105/thisisaaronland)

		ensure_changes := true
		skip_existing := false

		remote := c.Source + rel_path
		local := path.Join(c.Dest, rel_path)

		if !os.IsNotExist(err) {

			if skip_existing {
				c.Logger.Debug("%s already exists and we are skipping things that exist", local)
				continue
			}

			change, _ := c.HasChanged(local, remote)

			if !change {
				c.Logger.Info("no changes to %s", local)
				continue
			}
		}

		wg.Add(1)
		atomic.AddInt64(&c.Scheduled, 1)

		go func() {

			defer wg.Done()

			_, err = c.pool.SendWork(func() {

				cl_err := c.ClonePath(rel_path, ensure_changes)

				if cl_err != nil {
					atomic.AddInt64(&c.Error, 1)
				} else {
					atomic.AddInt64(&c.Success, 1)
				}

				atomic.AddInt64(&c.Completed, 1)
				c.Status()
			})

		}()
	}

	wg.Wait()
	return nil
}

func (c *WOFClone) ClonePath(rel_path string, ensure_changes bool) error {

	atomic.AddInt64(&c.Count, 1)

	remote := c.Source + rel_path
	local := path.Join(c.Dest, rel_path)

	_, err := os.Stat(local)

	if !os.IsNotExist(err) && ensure_changes {

		change, _ := c.HasChanged(local, remote)

		if !change {

			c.Logger.Debug("%s has not changed so skipping", local)
			atomic.AddInt64(&c.Skipped, 1)
			return nil
		}

	}

	process_err := c.Process(remote, local)

	if process_err != nil {
		atomic.AddInt64(&c.Error, 1)
		return process_err
	}

	return nil
}

// don't return true if there's a problem - move that logic up above

func (c *WOFClone) HasChanged(local string, remote string) (bool, error) {

	change := true

	body, err := ioutil.ReadFile(local)

	if err != nil {
		c.Logger.Error("Failed to read %s, becase %v", local, err)
		return change, err
	}

	hash := md5.Sum(body)
	local_hash := hex.EncodeToString(hash[:])

	return c.HasHashChanged(local_hash, remote)
}

func (c *WOFClone) HasHashChanged(local_hash string, remote string) (bool, error) {

	change := true

	rsp, err := c.Fetch("HEAD", remote)

	if err != nil {
		return change, err
	}

	defer rsp.Body.Close()

	etag := rsp.Header.Get("Etag")
	remote_hash := strings.Replace(etag, "\"", "", -1)

	if local_hash == remote_hash {
		change = false
	}

	return change, nil
}

func (c *WOFClone) Process(remote string, local string) error {

	c.Logger.Debug("fetch %s and store in %s", remote, local)

	local_root := path.Dir(local)

	_, err := os.Stat(local_root)

	if os.IsNotExist(err) {
		c.Logger.Info("create %s", local_root)
		os.MkdirAll(local_root, 0755)
	}

	rsp, fetch_err := c.Fetch("GET", remote)

	if fetch_err != nil {
		return fetch_err
	}

	defer rsp.Body.Close()

	contents, read_err := ioutil.ReadAll(rsp.Body)

	if read_err != nil {
		c.Logger.Error("failed to read body for %s, because %v", remote, read_err)
		return read_err
	}

	go func() error {

		write_err := ioutil.WriteFile(local, contents, 0644)

		if write_err != nil {
			c.Logger.Error("Failed to write %s, because %v", local, write_err)

			atomic.AddInt64(&c.Success, -1)
			atomic.AddInt64(&c.Error, 1)

			return write_err
		}

		c.Logger.Debug("Wrote %s to disk", local)
		return nil
	}()

	return nil
}

func (c *WOFClone) Fetch(method string, url string) (*http.Response, error) {

	c.Logger.Debug("%s %s", method, url)

	req, _ := http.NewRequest(method, url, nil)
	req.Close = true

	rsp, err := c.client.Do(req)

	if err != nil {
		c.Logger.Error("Failed to %s %s, because %v", method, url, err)
		// golog.Fatal(err)
		return nil, err
	}

	expected := 200

	if rsp.StatusCode != expected {
		c.Logger.Error("Failed to %s %s, because we expected %d from S3 and got '%s' instead", method, url, expected, rsp.Status)
		return nil, errors.New(rsp.Status)
	}

	return rsp, err
}

func (c *WOFClone) Status() {
	c.Logger.Info("scheduled: %d completed: %d success: %d error: %d goroutines: %d", c.Scheduled, c.Completed, c.Success, c.Error, runtime.NumGoroutine())
}
<|endoftext|>


<commit_before>package adcom1

// PodSequence identifies the slot position in pod field, for use in audio and video ad pods.
type SlotPositionInPod int8

// PodSequence options.
const (
	SlotPosLast        SlotPositionInPod = -1 // Last ad in the pod.
	SlotPosAny         SlotPositionInPod = 0  // Any ad in the pod.
	SlotPosFirst       SlotPositionInPod = 1  // First ad in the pod.
	SlotPosFirstOrLast SlotPositionInPod = 2  // First or Last ad in the pod.
)
<commit_msg>adcom1: fix godoc comment<commit_after>package adcom1

// SlotPositionInPod identifies the slot position in pod field, for use in audio and video ad pods.
type SlotPositionInPod int8

// SlotPositionInPod options.
const (
	SlotPosLast        SlotPositionInPod = -1 // Last ad in the pod.
	SlotPosAny         SlotPositionInPod = 0  // Any ad in the pod.
	SlotPosFirst       SlotPositionInPod = 1  // First ad in the pod.
	SlotPosFirstOrLast SlotPositionInPod = 2  // First or Last ad in the pod.
)
<|endoftext|>


<commit_before>package rtp

import (
	"math/rand"
	"time"
)

// Payloader payloads a byte array for use as rtp.Packet payloads
type Payloader interface {
	Payload(mtu int, payload []byte) [][]byte
}

// Packetizer packetizes a payload
type Packetizer interface {
	Packetize(payload []byte, samples uint32) []*Packet
}

type packetizer struct {
	MTU         int
	PayloadType uint8
	SSRC        uint32
	Payloader   Payloader
	Sequencer   Sequencer
	Timestamp   uint32
	ClockRate   uint32
}

// NewPacketizer returns a new instance of a Packetizer for a specific payloader
func NewPacketizer(mtu int, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
	rs := rand.NewSource(time.Now().UnixNano())
	r := rand.New(rs)

	return &packetizer{
		mtu,
		pt,
		ssrc,
		payloader,
		sequencer,
		r.Uint32(),
		clockRate,
	}
}

// Packetize packetizes the payload of an RTP packet and returns one or more RTP packets
func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
	payloads := p.Payloader.Payload(p.MTU-12, payload)
	packets := make([]*Packet, len(payloads))

	for i, pp := range payloads {
		packets[i] = &Packet{
			Version:        2,
			Padding:        false,
			Extension:      false,
			Marker:         i == len(payloads)-1,
			PayloadType:    p.PayloadType,
			SequenceNumber: p.Sequencer.NextSequenceNumber(),
			Timestamp:      p.Timestamp, // Figure out how to do timestamps
			SSRC:           p.SSRC,
			Payload:        pp,
		}
	}
	p.Timestamp += samples

	return packets
}
<commit_msg>Switch from individual linters to golangci-lint<commit_after>package rtp

import (
	"math/rand"
	"time"
)

// Payloader payloads a byte array for use as rtp.Packet payloads
type Payloader interface {
	Payload(mtu int, payload []byte) [][]byte
}

// Packetizer packetizes a payload
type Packetizer interface {
	Packetize(payload []byte, samples uint32) []*Packet
}

type packetizer struct {
	MTU         int
	PayloadType uint8
	SSRC        uint32
	Payloader   Payloader
	Sequencer   Sequencer
	Timestamp   uint32
	ClockRate   uint32
}

// NewPacketizer returns a new instance of a Packetizer for a specific payloader
func NewPacketizer(mtu int, pt uint8, ssrc uint32, payloader Payloader, sequencer Sequencer, clockRate uint32) Packetizer {
	rs := rand.NewSource(time.Now().UnixNano())
	r := rand.New(rs)

	return &packetizer{
		MTU:         mtu,
		PayloadType: pt,
		SSRC:        ssrc,
		Payloader:   payloader,
		Sequencer:   sequencer,
		Timestamp:   r.Uint32(),
		ClockRate:   clockRate,
	}
}

// Packetize packetizes the payload of an RTP packet and returns one or more RTP packets
func (p *packetizer) Packetize(payload []byte, samples uint32) []*Packet {
	payloads := p.Payloader.Payload(p.MTU-12, payload)
	packets := make([]*Packet, len(payloads))

	for i, pp := range payloads {
		packets[i] = &Packet{
			Version:        2,
			Padding:        false,
			Extension:      false,
			Marker:         i == len(payloads)-1,
			PayloadType:    p.PayloadType,
			SequenceNumber: p.Sequencer.NextSequenceNumber(),
			Timestamp:      p.Timestamp, // Figure out how to do timestamps
			SSRC:           p.SSRC,
			Payload:        pp,
		}
	}
	p.Timestamp += samples

	return packets
}
<|endoftext|>


<commit_before>package nad

import (
	"fmt"
	"regexp"
	"strings"
)

const prefix = "Main"

var commands = [...]string{
	"Main.Model?",
	"Main.Mute?",
	"Main.Mute=On",
	"Main.Mute=Off",
	"Main.Power?",
	"Main.Power=On",
	"Main.Power=Off",
	"Main.Source?",
	"Main.Source=CD",
	"Main.Source=TUNER",
	"Main.Source=VIDEO",
	"Main.Source=DISC/MDC",
	"Main.Source=TAPE2",
	"Main.Source=AUX",
	"Main.SpeakerA?",
	"Main.SpeakerA=On",
	"Main.SpeakerA=Off",
	"Main.SpeakerB?",
	"Main.SpeakerB=On",
	"Main.SpeakerB=Off",
	"Main.Tape1?",
	"Main.Tape1=On",
	"Main.Tape1=Off",
	"Main.Volume+",
	"Main.Volume-",
}

// Cmd represents a command sent to the amplifier.
type Cmd struct {
	Variable string
	Operator string
	Value    string
}

// Reply represents an reply received from the amplifier. A reply has the same fields as a command.
type Reply struct{ Cmd }

// String formats command as a string.
func (c *Cmd) String() string {
	return fmt.Sprint(prefix, ".", c.Variable, c.Operator, c.Value)
}

// Bytes returns a command as bytes.
func (c *Cmd) Bytes() []byte {
	return []byte(c.Delimited())
}

// Delimited formats command before sending it to amplifier.
func (c *Cmd) Delimited() string {
	return fmt.Sprint("\n", c.String(), "\n")
}

// Valid returns true if command is a command accepted by amplifier.
func (c *Cmd) Valid() bool {
	cmd := strings.ToLower(c.String())
	for _, c := range commands {
		if strings.ToLower(c) == cmd {
			return true
		}
	}
	return false
}

// ParseCmd parses s into a command.
func ParseCmd(s string) (Cmd, error) {
	s = strings.Trim(s, "\r\n")
	re := regexp.MustCompile("(?iU)^" + prefix + "\\." +
		"(.+)" +
		"([=+?-])" +
		"(.*)$")
	m := re.FindAllStringSubmatch(s, -1)
	if len(m) == 0 || len(m[0]) < 4 {
		return Cmd{}, fmt.Errorf("could not parse command: %q", s)
	}
	return Cmd{
		Variable: m[0][1],
		Operator: m[0][2],
		Value:    m[0][3],
	}, nil
}

// ParseReply parses b into a reply.
func ParseReply(b []byte) (Reply, error) {
	cmd, err := ParseCmd(string(b))
	if err != nil {
		return Reply{}, err
	}
	return Reply{cmd}, nil
}

// Cmds returns all valid commands.
func Cmds() [25]string {
	return commands
}
<commit_msg>Compile pattern at package level<commit_after>package nad

import (
	"fmt"
	"regexp"
	"strings"
)

const prefix = "Main"

var (
	commands = [...]string{
		"Main.Model?",
		"Main.Mute?",
		"Main.Mute=On",
		"Main.Mute=Off",
		"Main.Power?",
		"Main.Power=On",
		"Main.Power=Off",
		"Main.Source?",
		"Main.Source=CD",
		"Main.Source=TUNER",
		"Main.Source=VIDEO",
		"Main.Source=DISC/MDC",
		"Main.Source=TAPE2",
		"Main.Source=AUX",
		"Main.SpeakerA?",
		"Main.SpeakerA=On",
		"Main.SpeakerA=Off",
		"Main.SpeakerB?",
		"Main.SpeakerB=On",
		"Main.SpeakerB=Off",
		"Main.Tape1?",
		"Main.Tape1=On",
		"Main.Tape1=Off",
		"Main.Volume+",
		"Main.Volume-",
	}
	cmdPattern = regexp.MustCompile("(?iU)^" + prefix + "\\." +
		"(.+)" +
		"([=+?-])" +
		"(.*)$")
)

// Cmd represents a command sent to the amplifier.
type Cmd struct {
	Variable string
	Operator string
	Value    string
}

// Reply represents an reply received from the amplifier. A reply has the same fields as a command.
type Reply struct{ Cmd }

// String formats command as a string.
func (c *Cmd) String() string {
	return fmt.Sprint(prefix, ".", c.Variable, c.Operator, c.Value)
}

// Bytes returns a command as bytes.
func (c *Cmd) Bytes() []byte {
	return []byte(c.Delimited())
}

// Delimited formats command before sending it to amplifier.
func (c *Cmd) Delimited() string {
	return fmt.Sprint("\n", c.String(), "\n")
}

// Valid returns true if command is a command accepted by amplifier.
func (c *Cmd) Valid() bool {
	cmd := strings.ToLower(c.String())
	for _, c := range commands {
		if strings.ToLower(c) == cmd {
			return true
		}
	}
	return false
}

// ParseCmd parses s into a command.
func ParseCmd(s string) (Cmd, error) {
	s = strings.Trim(s, "\r\n")
	m := cmdPattern.FindAllStringSubmatch(s, -1)
	if len(m) == 0 || len(m[0]) < 4 {
		return Cmd{}, fmt.Errorf("could not parse command: %q", s)
	}
	return Cmd{
		Variable: m[0][1],
		Operator: m[0][2],
		Value:    m[0][3],
	}, nil
}

// ParseReply parses b into a reply.
func ParseReply(b []byte) (Reply, error) {
	cmd, err := ParseCmd(string(b))
	if err != nil {
		return Reply{}, err
	}
	return Reply{cmd}, nil
}

// Cmds returns all valid commands.
func Cmds() [25]string {
	return commands
}
<|endoftext|>


<commit_before>package net

//------------- cli -------------
//user
const CLI_CHECK string = "/user/check"
const CLI_LOGIN string = "/user/login"
const CLI_PING string = "/user/ping"

//article
const CLI_ART_LIST string = "/admin/arc/list"
const CLI_ART_ADD string = "/admin/arc/add"
const CLI_ART_RM string = "/admin/arc/del"
const CLI_ART_DOWN string = "/admin/arc/down"
const CLI_ART_MODIFY string = "/admin/art/modify"

//page
const CLI_PAGE_LIST string = "/admin/pag/list"
const CLI_PAGE_ADD string = "/admin/pag/add"
const CLI_PAGE_RM string = "/admin/pag/del"
const CLI_PAGE_DOWN string = "/admin/pag/down"
const CLI_PAGE_MODIFY string = "/admin/pag/modify"

//category
const CLI_CAT_LIST string = "/admin/cat/list"
const CLI_CAT_ADD string = "/admin/cat/add"
const CLI_CAT_RM string = "/admin/cat/del"
const CLI_CAT_MODIFY string = "/admin/cat/change"

//tweet
const CLI_TWEET_LIST string = "/admin/twe/list"
const CLI_TWEET_ADD string = "/admin/twe/add"
const CLI_TWEET_RM string = "/admin/twe/del"

//other
const CLI_CLEAN string = "/admin/clr/cache"
const CLI_OPTION string = "/admin/opt"

//------------- admin -------------

//user
const ADMIN_GROUP_USER string = "user"
const ADMIN_PING string = "ping"
const ADMIN_LOGIN string = "login"
const ADMIN_CHECK string = "check"

const ADMIN_GROUP_ADMIN string = "admin"

//article
const ADMIN_ART_ADD string = "arc/add"
const ADMIN_ART_RM string = "arc/del"
const ADMIN_ART_LIST string = "arc/list"
const ADMIN_ART_DOWNLOAD string = "arc/down"
const ADMIN_ART_MODIFY string = "art/modify"

//page
const ADMIN_PAGE_ADD string = "pag/add"
const ADMIN_PAGE_RM string = "pag/del"
const ADMIN_PAGE_LIST string = "pag/list"
const ADMIN_PAGE_DOWNLOAD string = "pag/down"
const ADMIN_PAGE_MODIFY string = "/pag/modify"

//tweet
const ADMIN_TWEET_ADD string = "twe/add"
const ADMIN_TWEET_RM string = "/twe/del"
const ADMIN_TWEET_LIST string = "/twe/list"

//category
const ADMIN_CAT_ADD string = "cat/add"
const ADMIN_CAT_RM string = "cat/del"
const ADMIN_CAT_LIST string = "cat/list"
const ADMIN_CAT_MODIFY string = "cat/change"

//other
const ADMIN_OPT string = "opt"
const ADMIN_CLEAN string = "clr/cache"

//------------- server -------------
const SERVER_INDEX string = "/"
const SERVER_ART string = "/archives/:id"
const SERVER_PAGE string = "/page/:id"
const SERVER_CAT string = "/category/:alias"
const SERVER_TWEET string = "/tweet/*page"
<commit_msg>change uri<commit_after>package net

//------------- cli -------------
//user
const CLI_CHECK string = "/user/check"
const CLI_LOGIN string = "/user/login"
const CLI_PING string = "/user/ping"

//article
const CLI_ART_LIST string = "/admin/art/list"
const CLI_ART_ADD string = "/admin/art/add"
const CLI_ART_RM string = "/admin/art/del"
const CLI_ART_DOWN string = "/admin/art/down"
const CLI_ART_MODIFY string = "/admin/art/modify"

//page
const CLI_PAGE_LIST string = "/admin/pag/list"
const CLI_PAGE_ADD string = "/admin/pag/add"
const CLI_PAGE_RM string = "/admin/pag/del"
const CLI_PAGE_DOWN string = "/admin/pag/down"
const CLI_PAGE_MODIFY string = "/admin/pag/modify"

//category
const CLI_CAT_LIST string = "/admin/cat/list"
const CLI_CAT_ADD string = "/admin/cat/add"
const CLI_CAT_RM string = "/admin/cat/del"
const CLI_CAT_MODIFY string = "/admin/cat/change"

//tweet
const CLI_TWEET_LIST string = "/admin/twe/list"
const CLI_TWEET_ADD string = "/admin/twe/add"
const CLI_TWEET_RM string = "/admin/twe/del"

//other
const CLI_CLEAN string = "/admin/clr/cache"
const CLI_OPTION string = "/admin/opt"

//------------- admin -------------

//user
const ADMIN_GROUP_USER string = "user"
const ADMIN_PING string = "ping"
const ADMIN_LOGIN string = "login"
const ADMIN_CHECK string = "check"

const ADMIN_GROUP_ADMIN string = "admin"

//article
const ADMIN_ART_ADD string = "art/add"
const ADMIN_ART_RM string = "art/del"
const ADMIN_ART_LIST string = "art/list"
const ADMIN_ART_DOWNLOAD string = "art/down"
const ADMIN_ART_MODIFY string = "art/modify"

//page
const ADMIN_PAGE_ADD string = "pag/add"
const ADMIN_PAGE_RM string = "pag/del"
const ADMIN_PAGE_LIST string = "pag/list"
const ADMIN_PAGE_DOWNLOAD string = "pag/down"
const ADMIN_PAGE_MODIFY string = "/pag/modify"

//tweet
const ADMIN_TWEET_ADD string = "twe/add"
const ADMIN_TWEET_RM string = "/twe/del"
const ADMIN_TWEET_LIST string = "/twe/list"

//category
const ADMIN_CAT_ADD string = "cat/add"
const ADMIN_CAT_RM string = "cat/del"
const ADMIN_CAT_LIST string = "cat/list"
const ADMIN_CAT_MODIFY string = "cat/change"

//other
const ADMIN_OPT string = "opt"
const ADMIN_CLEAN string = "clr/cache"

//------------- server -------------
const SERVER_INDEX string = "/"
const SERVER_ART string = "/archives/:id"
const SERVER_PAGE string = "/page/:id"
const SERVER_CAT string = "/category/:alias"
const SERVER_TWEET string = "/tweet/*page"
<|endoftext|>