<commit_before>package terraform

import (
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/dag"
)

func TestReferenceTransformer_simple(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"A"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefBasicStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceTransformer_self(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"A", "B"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefBasicStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceTransformer_path(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"A"},
	})
	g.Add(&graphNodeRefParentTest{
		NameValue: "child.A",
		PathValue: []string{"root", "child"},
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "child.B",
		PathValue: []string{"root", "child"},
		Refs:      []string{"A"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefPathStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceTransformer_backup(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"C/A"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefBackupStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceTransformer_backupPrimary(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"C/A"},
	})
	g.Add(&graphNodeRefParentTest{
		NameValue: "C",
		Names:     []string{"C"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefBackupPrimaryStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceTransformer_modulePath(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
		PathValue: []string{"foo"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"module.foo"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefModulePathStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceTransformer_modulePathNormalized(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
		PathValue: []string{"root", "foo"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"module.foo"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefModulePathStr)
	if actual != expected {
		t.Fatalf("bad:\n\n%s", actual)
	}
}

func TestReferenceMapReferences(t *testing.T) {
	cases := map[string]struct {
		Nodes  []dag.Vertex
		Check  dag.Vertex
		Result []string
	}{
		"simple": {
			Nodes: []dag.Vertex{
				&graphNodeRefParentTest{
					NameValue: "A",
					Names:     []string{"A"},
				},
			},
			Check: &graphNodeRefChildTest{
				NameValue: "foo",
				Refs:      []string{"A"},
			},
			Result: []string{"A"},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			rm := NewReferenceMap(tc.Nodes)
			result, _ := rm.References(tc.Check)

			var resultStr []string
			for _, v := range result {
				resultStr = append(resultStr, dag.VertexName(v))
			}

			sort.Strings(resultStr)
			sort.Strings(tc.Result)
			if !reflect.DeepEqual(resultStr, tc.Result) {
				t.Fatalf("bad: %#v", resultStr)
			}
		})
	}
}

func TestReferenceMapReferencedBy(t *testing.T) {
	cases := map[string]struct {
		Nodes  []dag.Vertex
		Check  dag.Vertex
		Result []string
	}{
		"simple": {
			Nodes: []dag.Vertex{
				&graphNodeRefChildTest{
					NameValue: "A",
					Refs:      []string{"A"},
				},
				&graphNodeRefChildTest{
					NameValue: "B",
					Refs:      []string{"A"},
				},
				&graphNodeRefChildTest{
					NameValue: "C",
					Refs:      []string{"B"},
				},
			},
			Check: &graphNodeRefParentTest{
				NameValue: "foo",
				Names:     []string{"A"},
			},
			Result: []string{"A", "B"},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			rm := NewReferenceMap(tc.Nodes)
			result := rm.Referrers(tc.Check)

			var resultStr []string
			for _, v := range result {
				resultStr = append(resultStr, dag.VertexName(v))
			}

			sort.Strings(resultStr)
			sort.Strings(tc.Result)
			if !reflect.DeepEqual(resultStr, tc.Result) {
				t.Fatalf("bad: %#v", resultStr)
			}
		})
	}
}

type graphNodeRefParentTest struct {
	NameValue string
	PathValue []string
	Names     []string
}

func (n *graphNodeRefParentTest) Name() string                { return n.NameValue }
func (n *graphNodeRefParentTest) ReferenceableName() []string { return n.Names }
func (n *graphNodeRefParentTest) Path() []string              { return n.PathValue }

type graphNodeRefChildTest struct {
	NameValue string
	PathValue []string
	Refs      []string
}

func (n *graphNodeRefChildTest) Name() string         { return n.NameValue }
func (n *graphNodeRefChildTest) References() []string { return n.Refs }
func (n *graphNodeRefChildTest) Path() []string       { return n.PathValue }

const testTransformRefBasicStr = `
A
B
  A
`

const testTransformRefBackupStr = `
A
B
  A
`

const testTransformRefBackupPrimaryStr = `
A
B
  C
C
`

const testTransformRefModulePathStr = `
A
B
  A
`

const testTransformRefPathStr = `
A
B
  A
child.A
child.B
  child.A
`
<commit_msg>core: Fix ReferenceTransformer tests<commit_after>package terraform

import (
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/dag"
)

func TestReferenceTransformer_simple(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"A"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefBasicStr)
	if actual != expected {
		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
	}
}

func TestReferenceTransformer_self(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"A", "B"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefBasicStr)
	if actual != expected {
		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
	}
}

func TestReferenceTransformer_path(t *testing.T) {
	g := Graph{Path: addrs.RootModuleInstance}
	g.Add(&graphNodeRefParentTest{
		NameValue: "A",
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "B",
		Refs:      []string{"A"},
	})
	g.Add(&graphNodeRefParentTest{
		NameValue: "child.A",
		PathValue: []string{"root", "child"},
		Names:     []string{"A"},
	})
	g.Add(&graphNodeRefChildTest{
		NameValue: "child.B",
		PathValue: []string{"root", "child"},
		Refs:      []string{"A"},
	})

	tf := &ReferenceTransformer{}
	if err := tf.Transform(&g); err != nil {
		t.Fatalf("err: %s", err)
	}

	actual := strings.TrimSpace(g.String())
	expected := strings.TrimSpace(testTransformRefPathStr)
	if actual != expected {
		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
	}
}

func TestReferenceMapReferences(t *testing.T) {
	cases := map[string]struct {
		Nodes  []dag.Vertex
		Check  dag.Vertex
		Result []string
	}{
		"simple": {
			Nodes: []dag.Vertex{
				&graphNodeRefParentTest{
					NameValue: "A",
					Names:     []string{"A"},
				},
			},
			Check: &graphNodeRefChildTest{
				NameValue: "foo",
				Refs:      []string{"A"},
			},
			Result: []string{"A"},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			rm := NewReferenceMap(tc.Nodes)
			result, _ := rm.References(tc.Check)

			var resultStr []string
			for _, v := range result {
				resultStr = append(resultStr, dag.VertexName(v))
			}

			sort.Strings(resultStr)
			sort.Strings(tc.Result)
			if !reflect.DeepEqual(resultStr, tc.Result) {
				t.Fatalf("bad: %#v", resultStr)
			}
		})
	}
}

func TestReferenceMapReferencedBy(t *testing.T) {
	cases := map[string]struct {
		Nodes  []dag.Vertex
		Check  dag.Vertex
		Result []string
	}{
		"simple": {
			Nodes: []dag.Vertex{
				&graphNodeRefChildTest{
					NameValue: "A",
					Refs:      []string{"A"},
				},
				&graphNodeRefChildTest{
					NameValue: "B",
					Refs:      []string{"A"},
				},
				&graphNodeRefChildTest{
					NameValue: "C",
					Refs:      []string{"B"},
				},
			},
			Check: &graphNodeRefParentTest{
				NameValue: "foo",
				Names:     []string{"A"},
			},
			Result: []string{"A", "B"},
		},
	}

	for tn, tc := range cases {
		t.Run(tn, func(t *testing.T) {
			rm := NewReferenceMap(tc.Nodes)
			result := rm.Referrers(tc.Check)

			var resultStr []string
			for _, v := range result {
				resultStr = append(resultStr, dag.VertexName(v))
			}

			sort.Strings(resultStr)
			sort.Strings(tc.Result)
			if !reflect.DeepEqual(resultStr, tc.Result) {
				t.Fatalf("bad: %#v", resultStr)
			}
		})
	}
}

type graphNodeRefParentTest struct {
	NameValue string
	PathValue []string
	Names     []string
}

var _ GraphNodeReferenceable = (*graphNodeRefParentTest)(nil)

func (n *graphNodeRefParentTest) Name() string {
	return n.NameValue
}

func (n *graphNodeRefParentTest) ReferenceableAddrs() []addrs.Referenceable {
	ret := make([]addrs.Referenceable, len(n.Names))
	for i, name := range n.Names {
		ret[i] = addrs.LocalValue{Name: name}
	}
	return ret
}

func (n *graphNodeRefParentTest) Path() addrs.ModuleInstance {
	return normalizeModulePath(n.PathValue)
}

type graphNodeRefChildTest struct {
	NameValue string
	PathValue []string
	Refs      []string
}

var _ GraphNodeReferencer = (*graphNodeRefChildTest)(nil)

func (n *graphNodeRefChildTest) Name() string {
	return n.NameValue
}

func (n *graphNodeRefChildTest) References() []*addrs.Reference {
	ret := make([]*addrs.Reference, len(n.Refs))
	for i, name := range n.Refs {
		ret[i] = &addrs.Reference{
			Subject: addrs.LocalValue{Name: name},
		}
	}
	return ret
}

func (n *graphNodeRefChildTest) Path() addrs.ModuleInstance {
	return normalizeModulePath(n.PathValue)
}

const testTransformRefBasicStr = `
A
B
  A
`

const testTransformRefBackupStr = `
A
B
  A
`

const testTransformRefBackupPrimaryStr = `
A
B
  C
C
`

const testTransformRefModulePathStr = `
A
B
  A
`

const testTransformRefPathStr = `
A
B
  A
child.A
child.B
  child.A
`
<|endoftext|>


<commit_before>// +build !wasm

package input

import (
	"context"
	"fmt"
	"time"

	"github.com/Azure/azure-storage-queue-go/azqueue"
	"github.com/Jeffail/benthos/v3/internal/service/azure"
	"github.com/Jeffail/benthos/v3/lib/input/reader"
	"github.com/Jeffail/benthos/v3/lib/log"
	"github.com/Jeffail/benthos/v3/lib/message"
	"github.com/Jeffail/benthos/v3/lib/metrics"
	"github.com/Jeffail/benthos/v3/lib/types"
)

// AzureQueueStorage is a benthos reader.Type implementation that reads messages
// from an Azure Queue Storage container.
type azureQueueStorage struct {
	conf AzureQueueStorageConfig

	queueURL *azqueue.QueueURL

	log   log.Modular
	stats metrics.Type
}

// newAzureQueueStorage creates a new Azure Storage Queue input type.
func newAzureQueueStorage(conf AzureQueueStorageConfig, log log.Modular, stats metrics.Type) (*azureQueueStorage, error) {
	serviceURL, err := azure.GetQueueServiceURL(conf.StorageAccount, conf.StorageAccessKey, conf.StorageConnectionString)
	if err != nil {
		return nil, err
	}
	queueURL := serviceURL.NewQueueURL(conf.QueueName)
	a := &azureQueueStorage{
		conf:     conf,
		log:      log,
		stats:    stats,
		queueURL: &queueURL,
	}
	return a, nil
}

// ConnectWithContext attempts to establish a connection
func (a *azureQueueStorage) ConnectWithContext(ctx context.Context) error {
	return nil
}

// ReadWithContext attempts to read a new message from the target Azure Storage Queue
// Storage container.
func (a *azureQueueStorage) ReadWithContext(ctx context.Context) (msg types.Message, ackFn reader.AsyncAckFn, err error) {
	messageURL := a.queueURL.NewMessagesURL()
	dequeue, err := messageURL.Dequeue(ctx, 1, 30*time.Second)
	if err != nil {
		if cerr, ok := err.(azqueue.StorageError); ok {
			if cerr.ServiceCode() == azqueue.ServiceCodeQueueNotFound {
				ctx := context.Background()
				_, err = a.queueURL.Create(ctx, azqueue.Metadata{})
				return nil, nil, err
			}
			return nil, nil, fmt.Errorf("storage error message: %v", cerr)
		}
		return nil, nil, fmt.Errorf("error dequeing message: %v", err)
	}
	if n := dequeue.NumMessages(); n > 0 {
		props, _ := a.queueURL.GetProperties(ctx)
		metadata := props.NewMetadata()
		msg := message.New(nil)
		for m := int32(0); m < dequeue.NumMessages(); m++ {
			queueMsg := dequeue.Message(m)
			part := message.NewPart([]byte(queueMsg.Text))
			msg.Append(part)
			meta := msg.Get(0).Metadata()
			meta.Set("queue_storage_insertion_time", queueMsg.InsertionTime.Format(time.RFC3339))
			for k, v := range metadata {
				meta.Set(k, v)
			}
			msgIDURL := messageURL.NewMessageIDURL(queueMsg.ID)
			_, err = msgIDURL.Delete(ctx, queueMsg.PopReceipt)
		}
		return msg, func(rctx context.Context, res types.Response) error {
			return nil
		}, nil
	}
	return nil, nil, nil
}

// CloseAsync begins cleaning up resources used by this reader asynchronously.
func (a *azureQueueStorage) CloseAsync() {
}

// WaitForClose will block until either the reader is closed or a specified
// timeout occurs.
func (a *azureQueueStorage) WaitForClose(time.Duration) error {
	return nil
}

//------------------------------------------------------------------------------
<commit_msg>Fix azure storage queue input acknowledge (#689)<commit_after>// +build !wasm

package input

import (
	"context"
	"fmt"
	"time"

	"github.com/Azure/azure-storage-queue-go/azqueue"
	"github.com/Jeffail/benthos/v3/internal/service/azure"
	"github.com/Jeffail/benthos/v3/lib/input/reader"
	"github.com/Jeffail/benthos/v3/lib/log"
	"github.com/Jeffail/benthos/v3/lib/message"
	"github.com/Jeffail/benthos/v3/lib/metrics"
	"github.com/Jeffail/benthos/v3/lib/types"
)

// AzureQueueStorage is a benthos reader.Type implementation that reads messages
// from an Azure Queue Storage container.
type azureQueueStorage struct {
	conf AzureQueueStorageConfig

	queueURL *azqueue.QueueURL

	log   log.Modular
	stats metrics.Type
}

// newAzureQueueStorage creates a new Azure Storage Queue input type.
func newAzureQueueStorage(conf AzureQueueStorageConfig, log log.Modular, stats metrics.Type) (*azureQueueStorage, error) {
	serviceURL, err := azure.GetQueueServiceURL(conf.StorageAccount, conf.StorageAccessKey, conf.StorageConnectionString)
	if err != nil {
		return nil, err
	}
	queueURL := serviceURL.NewQueueURL(conf.QueueName)
	a := &azureQueueStorage{
		conf:     conf,
		log:      log,
		stats:    stats,
		queueURL: &queueURL,
	}
	return a, nil
}

// ConnectWithContext attempts to establish a connection
func (a *azureQueueStorage) ConnectWithContext(ctx context.Context) error {
	return nil
}

// ReadWithContext attempts to read a new message from the target Azure Storage Queue
// Storage container.
func (a *azureQueueStorage) ReadWithContext(ctx context.Context) (msg types.Message, ackFn reader.AsyncAckFn, err error) {
	messageURL := a.queueURL.NewMessagesURL()
	dequeue, err := messageURL.Dequeue(ctx, 1, 30*time.Second)
	if err != nil {
		if cerr, ok := err.(azqueue.StorageError); ok {
			if cerr.ServiceCode() == azqueue.ServiceCodeQueueNotFound {
				ctx := context.Background()
				_, err = a.queueURL.Create(ctx, azqueue.Metadata{})
				return nil, nil, err
			}
			return nil, nil, fmt.Errorf("storage error message: %v", cerr)
		}
		return nil, nil, fmt.Errorf("error dequeing message: %v", err)
	}
	if n := dequeue.NumMessages(); n > 0 {
		props, _ := a.queueURL.GetProperties(ctx)
		metadata := props.NewMetadata()
		msg := message.New(nil)
		dqm := make([]*azqueue.DequeuedMessage, n)
		for i := int32(0); i < n; i++ {
			queueMsg := dequeue.Message(i)
			part := message.NewPart([]byte(queueMsg.Text))
			meta := part.Metadata()
			meta.Set("queue_storage_insertion_time", queueMsg.InsertionTime.Format(time.RFC3339))
			for k, v := range metadata {
				meta.Set(k, v)
			}
			msg.Append(part)
			dqm[i] = queueMsg
		}
		return msg, func(rctx context.Context, res types.Response) error {
			for i := int32(0); i < n; i++ {
				msgIDURL := messageURL.NewMessageIDURL(dqm[i].ID)
				_, err = msgIDURL.Delete(ctx, dqm[i].PopReceipt)
				if err != nil {
					return fmt.Errorf("error deleting message: %v", err)
				}
			}
			return nil
		}, nil
	}
	return nil, nil, nil
}

// CloseAsync begins cleaning up resources used by this reader asynchronously.
func (a *azureQueueStorage) CloseAsync() {
}

// WaitForClose will block until either the reader is closed or a specified
// timeout occurs.
func (a *azureQueueStorage) WaitForClose(time.Duration) error {
	return nil
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>package upcloud

import (
	"context"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	"strconv"
)

func resourceUpCloudFirewallRules() *schema.Resource {
	return &schema.Resource{
		CreateContext: resourceUpCloudFirewallRulesCreate,
		ReadContext:   resourceUpCloudFirewallRulesRead,
		UpdateContext: resourceUpCloudFirewallRulesUpdate,
		DeleteContext: resourceUpCloudFirewallRulesDelete,
		Importer: &schema.ResourceImporter{
			StateContext: schema.ImportStatePassthroughContext,
		},
		Schema: map[string]*schema.Schema{
			"server_id": {
				Type:        schema.TypeString,
				Description: "The unique id of the server to be protected the firewall rules",
				Required:    true,
				ForceNew:    true,
			},
			"firewall_rule": {
				Type:     schema.TypeList,
				MaxItems: 1000,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"direction": {
							Type:         schema.TypeString,
							Description:  "The direction of network traffic this rule will be applied to",
							Required:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringInSlice([]string{"in", "out"}, false),
						},
						"action": {
							Type:         schema.TypeString,
							Description:  "Action to take if the rule conditions are met",
							Required:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringInSlice([]string{"accept", "drop"}, false),
						},
						"family": {
							Type:         schema.TypeString,
							Description:  "The address family of new firewall rule",
							Required:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6"}, false),
						},
						"protocol": {
							Type:         schema.TypeString,
							Description:  "The protocol this rule will be applied to",
							Optional:     true,
							ForceNew:     true,
							Default:      "tcp",
							ValidateFunc: validation.StringInSlice([]string{"tcp", "udp", "icmp"}, false),
						},
						"icmp_type": {
							Type:         schema.TypeString,
							Description:  "The ICMP type",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringLenBetween(0, 255),
						},
						"source_address_start": {
							Type:         schema.TypeString,
							Description:  "The source address range starts from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"source_address_end": {
							Type:         schema.TypeString,
							Description:  "The source address range ends from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"source_port_end": {
							Type:         schema.TypeInt,
							Description:  "The source port range ends from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"source_port_start": {
							Type:         schema.TypeInt,
							Description:  "The source port range starts from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"destination_address_start": {
							Type:         schema.TypeString,
							Description:  "The destination address range starts from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"destination_address_end": {
							Type:         schema.TypeString,
							Description:  "The destination address range ends from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"destination_port_start": {
							Type:         schema.TypeInt,
							Description:  "The destination port range starts from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"destination_port_end": {
							Type:         schema.TypeInt,
							Description:  "The destination port range ends from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"comment": {
							Type:         schema.TypeString,
							Description:  "Freeform comment string for the rule",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringLenBetween(0, 250),
						},
					},
				},
			},
		},
	}
}

func resourceUpCloudFirewallRulesCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	opts := &request.CreateFirewallRulesRequest{
		ServerUUID: d.Get("server_id").(string),
	}

	if v, ok := d.GetOk("firewall_rule"); ok {

		var firewallRules []upcloud.FirewallRule

		for _, frMap := range v.([]interface{}) {
			rule := frMap.(map[string]interface{})

			destinationPortStart := strconv.Itoa(rule["destination_port_start"].(int))
			if destinationPortStart == "0" {
				destinationPortStart = ""
			}

			destinationPortEnd := strconv.Itoa(rule["destination_port_end"].(int))
			if destinationPortEnd == "0" {
				destinationPortEnd = ""
			}

			sourcePortStart := strconv.Itoa(rule["source_port_start"].(int))
			if sourcePortStart == "0" {
				sourcePortStart = ""
			}

			sourcePortEnd := strconv.Itoa(rule["source_port_end"].(int))
			if sourcePortEnd == "0" {
				sourcePortEnd = ""
			}

			firewallRule := upcloud.FirewallRule{
				Action:                  rule["action"].(string),
				Comment:                 rule["comment"].(string),
				DestinationAddressStart: rule["destination_address_start"].(string),
				DestinationAddressEnd:   rule["destination_address_end"].(string),
				DestinationPortStart:    destinationPortStart,
				DestinationPortEnd:      destinationPortEnd,
				Direction:               rule["direction"].(string),
				Family:                  rule["family"].(string),
				ICMPType:                rule["icmp_type"].(string),
				Protocol:                rule["protocol"].(string),
				SourceAddressStart:      rule["source_address_start"].(string),
				SourceAddressEnd:        rule["source_address_end"].(string),
				SourcePortStart:         sourcePortStart,
				SourcePortEnd:           sourcePortEnd,
			}

			firewallRules = append(firewallRules, firewallRule)
		}

		opts.FirewallRules = firewallRules
	}

	err := client.CreateFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	d.SetId(d.Get("server_id").(string))

	return resourceUpCloudFirewallRulesRead(ctx, d, meta)
}

func resourceUpCloudFirewallRulesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	var diags diag.Diagnostics

	opts := &request.GetFirewallRulesRequest{
		ServerUUID: d.Id(),
	}

	firewallRules, err := client.GetFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	var frMaps []map[string]interface{}

	for _, rule := range firewallRules.FirewallRules {

		frMap := map[string]interface{}{
			"action":                    rule.Action,
			"comment":                   rule.Comment,
			"destination_address_end":   rule.DestinationAddressEnd,
			"destination_address_start": rule.DestinationAddressStart,
			"direction":                 rule.Direction,
			"family":                    rule.Family,
			"icmp_type":                 rule.ICMPType,
			"protocol":                  rule.Protocol,
			"source_address_end":        rule.SourceAddressEnd,
			"source_address_start":      rule.SourceAddressStart,
		}

		if rule.DestinationPortEnd != "" {
			value, err := strconv.Atoi(rule.DestinationPortEnd)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["destination_port_end"] = value
		}

		if rule.DestinationPortStart != "" {
			value, err := strconv.Atoi(rule.DestinationPortStart)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["destination_port_start"] = value
		}

		if rule.SourcePortEnd != "" {
			value, err := strconv.Atoi(rule.SourcePortEnd)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["source_port_end"] = value
		}

		if rule.SourcePortStart != "" {
			value, err := strconv.Atoi(rule.SourcePortStart)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["source_port_start"] = value
		}

		frMaps = append(frMaps, frMap)
	}

	if err := d.Set("firewall_rule", frMaps); err != nil {
		return diag.FromErr(err)
	}

	return diags
}

func resourceUpCloudFirewallRulesUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	opts := &request.CreateFirewallRulesRequest{
		ServerUUID: d.Id(),
	}

	err := client.CreateFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	if d.HasChange("firewall_rule") {

		v := d.Get("firewall_rule")
		var firewallRules []upcloud.FirewallRule

		for _, frMap := range v.([]interface{}) {
			rule := frMap.(map[string]interface{})

			destinationPortStart := strconv.Itoa(rule["destination_port_start"].(int))
			if destinationPortStart == "0" {
				destinationPortStart = ""
			}

			destinationPortEnd := strconv.Itoa(rule["destination_port_end"].(int))
			if destinationPortEnd == "0" {
				destinationPortEnd = ""
			}

			sourcePortStart := strconv.Itoa(rule["source_port_start"].(int))
			if sourcePortStart == "0" {
				sourcePortStart = ""
			}

			sourcePortEnd := strconv.Itoa(rule["source_port_end"].(int))
			if sourcePortEnd == "0" {
				sourcePortEnd = ""
			}

			firewallRule := upcloud.FirewallRule{
				Action:                  rule["action"].(string),
				Comment:                 rule["comment"].(string),
				DestinationAddressStart: rule["destination_address_start"].(string),
				DestinationAddressEnd:   rule["destination_address_end"].(string),
				DestinationPortStart:    destinationPortStart,
				DestinationPortEnd:      destinationPortEnd,
				Direction:               rule["direction"].(string),
				Family:                  rule["family"].(string),
				ICMPType:                rule["icmp_type"].(string),
				Protocol:                rule["protocol"].(string),
				SourceAddressStart:      rule["source_address_start"].(string),
				SourceAddressEnd:        rule["source_address_end"].(string),
				SourcePortStart:         sourcePortStart,
				SourcePortEnd:           sourcePortEnd,
			}

			firewallRules = append(firewallRules, firewallRule)
		}

		opts.FirewallRules = firewallRules
	}

	err = client.CreateFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	return resourceUpCloudFirewallRulesRead(ctx, d, meta)
}

func resourceUpCloudFirewallRulesDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	var diags diag.Diagnostics

	opts := &request.CreateFirewallRulesRequest{
		ServerUUID:    d.Id(),
		FirewallRules: nil,
	}

	err := client.CreateFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	d.SetId("")
	return diags
}
<commit_msg>fix: firewall rules cannot be modified when the server is in `maintenance` state (#98)<commit_after>package upcloud

import (
	"context"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud/request"
	"github.com/UpCloudLtd/upcloud-go-api/upcloud/service"
	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
	"strconv"
	"time"
)

func resourceUpCloudFirewallRules() *schema.Resource {
	return &schema.Resource{
		CreateContext: resourceUpCloudFirewallRulesCreate,
		ReadContext:   resourceUpCloudFirewallRulesRead,
		UpdateContext: resourceUpCloudFirewallRulesUpdate,
		DeleteContext: resourceUpCloudFirewallRulesDelete,
		Importer: &schema.ResourceImporter{
			StateContext: schema.ImportStatePassthroughContext,
		},
		Schema: map[string]*schema.Schema{
			"server_id": {
				Type:        schema.TypeString,
				Description: "The unique id of the server to be protected the firewall rules",
				Required:    true,
				ForceNew:    true,
			},
			"firewall_rule": {
				Type:     schema.TypeList,
				MaxItems: 1000,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"direction": {
							Type:         schema.TypeString,
							Description:  "The direction of network traffic this rule will be applied to",
							Required:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringInSlice([]string{"in", "out"}, false),
						},
						"action": {
							Type:         schema.TypeString,
							Description:  "Action to take if the rule conditions are met",
							Required:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringInSlice([]string{"accept", "drop"}, false),
						},
						"family": {
							Type:         schema.TypeString,
							Description:  "The address family of new firewall rule",
							Required:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringInSlice([]string{"IPv4", "IPv6"}, false),
						},
						"protocol": {
							Type:         schema.TypeString,
							Description:  "The protocol this rule will be applied to",
							Optional:     true,
							ForceNew:     true,
							Default:      "tcp",
							ValidateFunc: validation.StringInSlice([]string{"tcp", "udp", "icmp"}, false),
						},
						"icmp_type": {
							Type:         schema.TypeString,
							Description:  "The ICMP type",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringLenBetween(0, 255),
						},
						"source_address_start": {
							Type:         schema.TypeString,
							Description:  "The source address range starts from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"source_address_end": {
							Type:         schema.TypeString,
							Description:  "The source address range ends from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"source_port_end": {
							Type:         schema.TypeInt,
							Description:  "The source port range ends from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"source_port_start": {
							Type:         schema.TypeInt,
							Description:  "The source port range starts from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"destination_address_start": {
							Type:         schema.TypeString,
							Description:  "The destination address range starts from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"destination_address_end": {
							Type:         schema.TypeString,
							Description:  "The destination address range ends from this address",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.Any(validation.IsIPv4Address, validation.IsIPv6Address, validation.StringIsEmpty),
						},
						"destination_port_start": {
							Type:         schema.TypeInt,
							Description:  "The destination port range starts from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"destination_port_end": {
							Type:         schema.TypeInt,
							Description:  "The destination port range ends from this port number",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.IsPortNumber,
						},
						"comment": {
							Type:         schema.TypeString,
							Description:  "Freeform comment string for the rule",
							Optional:     true,
							ForceNew:     true,
							ValidateFunc: validation.StringLenBetween(0, 250),
						},
					},
				},
			},
		},
	}
}

func resourceUpCloudFirewallRulesCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	opts := &request.CreateFirewallRulesRequest{
		ServerUUID: d.Get("server_id").(string),
	}

	if v, ok := d.GetOk("firewall_rule"); ok {

		var firewallRules []upcloud.FirewallRule

		for _, frMap := range v.([]interface{}) {
			rule := frMap.(map[string]interface{})

			destinationPortStart := strconv.Itoa(rule["destination_port_start"].(int))
			if destinationPortStart == "0" {
				destinationPortStart = ""
			}

			destinationPortEnd := strconv.Itoa(rule["destination_port_end"].(int))
			if destinationPortEnd == "0" {
				destinationPortEnd = ""
			}

			sourcePortStart := strconv.Itoa(rule["source_port_start"].(int))
			if sourcePortStart == "0" {
				sourcePortStart = ""
			}

			sourcePortEnd := strconv.Itoa(rule["source_port_end"].(int))
			if sourcePortEnd == "0" {
				sourcePortEnd = ""
			}

			firewallRule := upcloud.FirewallRule{
				Action:                  rule["action"].(string),
				Comment:                 rule["comment"].(string),
				DestinationAddressStart: rule["destination_address_start"].(string),
				DestinationAddressEnd:   rule["destination_address_end"].(string),
				DestinationPortStart:    destinationPortStart,
				DestinationPortEnd:      destinationPortEnd,
				Direction:               rule["direction"].(string),
				Family:                  rule["family"].(string),
				ICMPType:                rule["icmp_type"].(string),
				Protocol:                rule["protocol"].(string),
				SourceAddressStart:      rule["source_address_start"].(string),
				SourceAddressEnd:        rule["source_address_end"].(string),
				SourcePortStart:         sourcePortStart,
				SourcePortEnd:           sourcePortEnd,
			}

			firewallRules = append(firewallRules, firewallRule)
		}

		opts.FirewallRules = firewallRules
	}

	if _, err := client.WaitForServerState(&request.WaitForServerStateRequest{
		UUID:           opts.ServerUUID,
		UndesiredState: upcloud.ServerStateMaintenance,
		Timeout:        time.Minute * 5,
	}); err != nil {
		return diag.FromErr(err)
	}

	if err := client.CreateFirewallRules(opts); err != nil {
		return diag.FromErr(err)
	}

	d.SetId(d.Get("server_id").(string))

	return resourceUpCloudFirewallRulesRead(ctx, d, meta)
}

func resourceUpCloudFirewallRulesRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	var diags diag.Diagnostics

	opts := &request.GetFirewallRulesRequest{
		ServerUUID: d.Id(),
	}

	firewallRules, err := client.GetFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	var frMaps []map[string]interface{}

	for _, rule := range firewallRules.FirewallRules {

		frMap := map[string]interface{}{
			"action":                    rule.Action,
			"comment":                   rule.Comment,
			"destination_address_end":   rule.DestinationAddressEnd,
			"destination_address_start": rule.DestinationAddressStart,
			"direction":                 rule.Direction,
			"family":                    rule.Family,
			"icmp_type":                 rule.ICMPType,
			"protocol":                  rule.Protocol,
			"source_address_end":        rule.SourceAddressEnd,
			"source_address_start":      rule.SourceAddressStart,
		}

		if rule.DestinationPortEnd != "" {
			value, err := strconv.Atoi(rule.DestinationPortEnd)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["destination_port_end"] = value
		}

		if rule.DestinationPortStart != "" {
			value, err := strconv.Atoi(rule.DestinationPortStart)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["destination_port_start"] = value
		}

		if rule.SourcePortEnd != "" {
			value, err := strconv.Atoi(rule.SourcePortEnd)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["source_port_end"] = value
		}

		if rule.SourcePortStart != "" {
			value, err := strconv.Atoi(rule.SourcePortStart)
			if err != nil {
				diag.FromErr(err)
			}
			frMap["source_port_start"] = value
		}

		frMaps = append(frMaps, frMap)
	}

	if err := d.Set("firewall_rule", frMaps); err != nil {
		return diag.FromErr(err)
	}

	return diags
}

func resourceUpCloudFirewallRulesUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	opts := &request.CreateFirewallRulesRequest{
		ServerUUID: d.Id(),
	}

	err := client.CreateFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	if d.HasChange("firewall_rule") {

		v := d.Get("firewall_rule")
		var firewallRules []upcloud.FirewallRule

		for _, frMap := range v.([]interface{}) {
			rule := frMap.(map[string]interface{})

			destinationPortStart := strconv.Itoa(rule["destination_port_start"].(int))
			if destinationPortStart == "0" {
				destinationPortStart = ""
			}

			destinationPortEnd := strconv.Itoa(rule["destination_port_end"].(int))
			if destinationPortEnd == "0" {
				destinationPortEnd = ""
			}

			sourcePortStart := strconv.Itoa(rule["source_port_start"].(int))
			if sourcePortStart == "0" {
				sourcePortStart = ""
			}

			sourcePortEnd := strconv.Itoa(rule["source_port_end"].(int))
			if sourcePortEnd == "0" {
				sourcePortEnd = ""
			}

			firewallRule := upcloud.FirewallRule{
				Action:                  rule["action"].(string),
				Comment:                 rule["comment"].(string),
				DestinationAddressStart: rule["destination_address_start"].(string),
				DestinationAddressEnd:   rule["destination_address_end"].(string),
				DestinationPortStart:    destinationPortStart,
				DestinationPortEnd:      destinationPortEnd,
				Direction:               rule["direction"].(string),
				Family:                  rule["family"].(string),
				ICMPType:                rule["icmp_type"].(string),
				Protocol:                rule["protocol"].(string),
				SourceAddressStart:      rule["source_address_start"].(string),
				SourceAddressEnd:        rule["source_address_end"].(string),
				SourcePortStart:         sourcePortStart,
				SourcePortEnd:           sourcePortEnd,
			}

			firewallRules = append(firewallRules, firewallRule)
		}

		opts.FirewallRules = firewallRules
	}

	err = client.CreateFirewallRules(opts)

	if err != nil {
		return diag.FromErr(err)
	}

	return resourceUpCloudFirewallRulesRead(ctx, d, meta)
}

func resourceUpCloudFirewallRulesDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*service.Service)

	var diags diag.Diagnostics

	opts := &request.CreateFirewallRulesRequest{
		ServerUUID:    d.Id(),
		FirewallRules: nil,
	}

	if _, err := client.WaitForServerState(&request.WaitForServerStateRequest{
		UUID:           opts.ServerUUID,
		UndesiredState: upcloud.ServerStateMaintenance,
		Timeout:        time.Minute * 5,
	}); err != nil {
		return diag.FromErr(err)
	}

	if err := client.CreateFirewallRules(opts); err != nil {
		return diag.FromErr(err)
	}

	d.SetId("")
	return diags
}
<|endoftext|>


<commit_before>package redis2

import (
	"context"
	"fmt"
	"time"

	"github.com/go-redis/redis"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
)

const (
	DIR_LIST_MARKER = "\x00"
)

type UniversalRedis2Store struct {
	Client redis.UniversalClient
	superLargeDirectoryHash map[string]string
}

func (store *UniversalRedis2Store) isSuperLargeDirectory(dir string) (dirHash string, isSuperLargeDirectory bool) {
	dirHash, isSuperLargeDirectory = store.superLargeDirectoryHash[dir]
	return
}

func (store *UniversalRedis2Store) loadSuperLargeDirectories(superLargeDirectories []string) {
	// set directory hash
	store.superLargeDirectoryHash = make(map[string]string)
	existingHash := make(map[string]string)
	for _, dir := range superLargeDirectories {
		// adding dir hash to avoid duplicated names
		dirHash := util.Md5String([]byte(dir))[:4]
		store.superLargeDirectoryHash[dir] = dirHash
		if existingDir, found := existingHash[dirHash]; found {
			glog.Fatalf("directory %s has the same hash as %s", dir, existingDir)
		}
		existingHash[dirHash] = dir
	}
}

func (store *UniversalRedis2Store) BeginTransaction(ctx context.Context) (context.Context, error) {
	return ctx, nil
}
func (store *UniversalRedis2Store) CommitTransaction(ctx context.Context) error {
	return nil
}
func (store *UniversalRedis2Store) RollbackTransaction(ctx context.Context) error {
	return nil
}

func (store *UniversalRedis2Store) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) {

	value, err := entry.EncodeAttributesAndChunks()
	if err != nil {
		return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
	}

	if len(entry.Chunks) > 50 {
		value = util.MaybeGzipData(value)
	}

	if err = store.Client.Set(string(entry.FullPath), value, time.Duration(entry.TtlSec)*time.Second).Err(); err != nil {
		return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
	}

	dir, name := entry.FullPath.DirAndName()
	if _, found := store.isSuperLargeDirectory(dir); found {
		return nil
	}

	if name != "" {
		if err = store.Client.ZAddNX(genDirectoryListKey(dir), redis.Z{Score: 0, Member: name}).Err(); err != nil {
			return fmt.Errorf("persisting %s in parent dir: %v", entry.FullPath, err)
		}
	}

	return nil
}

func (store *UniversalRedis2Store) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) {

	return store.InsertEntry(ctx, entry)
}

func (store *UniversalRedis2Store) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) {

	data, err := store.Client.Get(string(fullpath)).Result()
	if err == redis.Nil {
		return nil, filer_pb.ErrNotFound
	}

	if err != nil {
		return nil, fmt.Errorf("get %s : %v", fullpath, err)
	}

	entry = &filer.Entry{
		FullPath: fullpath,
	}
	err = entry.DecodeAttributesAndChunks(util.MaybeDecompressData([]byte(data)))
	if err != nil {
		return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
	}

	return entry, nil
}

func (store *UniversalRedis2Store) DeleteEntry(ctx context.Context, fullpath util.FullPath) (err error) {

	_, err = store.Client.Del(genDirectoryListKey(string(fullpath))).Result()
	if err != nil {
		return fmt.Errorf("delete dir list %s : %v", fullpath, err)
	}

	_, err = store.Client.Del(string(fullpath)).Result()
	if err != nil {
		return fmt.Errorf("delete %s : %v", fullpath, err)
	}

	dir, name := fullpath.DirAndName()
	if _, found := store.isSuperLargeDirectory(dir); found {
		return nil
	}
	if name != "" {
		_, err = store.Client.ZRem(genDirectoryListKey(dir), name).Result()
		if err != nil {
			return fmt.Errorf("DeleteEntry %s in parent dir: %v", fullpath, err)
		}
	}

	return nil
}

func (store *UniversalRedis2Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) {

	if _, found := store.isSuperLargeDirectory(string(fullpath)); found {
		return nil
	}

	members, err := store.Client.ZRange(genDirectoryListKey(string(fullpath)), 0, -1).Result()
	if err != nil {
		return fmt.Errorf("DeleteFolderChildren %s : %v", fullpath, err)
	}

	for _, fileName := range members {
		path := util.NewFullPath(string(fullpath), fileName)
		_, err = store.Client.Del(string(path)).Result()
		if err != nil {
			return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err)
		}
	}

	return nil
}

func (store *UniversalRedis2Store) ListDirectoryPrefixedEntries(ctx context.Context, fullpath util.FullPath, startFileName string, inclusive bool, limit int, prefix string) (entries []*filer.Entry, err error) {
	return nil, filer.ErrUnsupportedListDirectoryPrefixed
}

func (store *UniversalRedis2Store) ListDirectoryEntries(ctx context.Context, fullpath util.FullPath, startFileName string, inclusive bool,
	limit int) (entries []*filer.Entry, err error) {

	dirListKey := genDirectoryListKey(string(fullpath))
	start := int64(0)
	if startFileName != "" {
		start, _ = store.Client.ZRank(dirListKey, startFileName).Result()
		if !inclusive {
			start++
		}
	}
	members, err := store.Client.ZRange(dirListKey, start, start+int64(limit)-1).Result()
	if err != nil {
		return nil, fmt.Errorf("list %s : %v", fullpath, err)
	}

	// fetch entry meta
	for _, fileName := range members {
		path := util.NewFullPath(string(fullpath), fileName)
		entry, err := store.FindEntry(ctx, path)
		if err != nil {
			glog.V(0).Infof("list %s : %v", path, err)
		} else {
			if entry.TtlSec > 0 {
				if entry.Attr.Crtime.Add(time.Duration(entry.TtlSec) * time.Second).Before(time.Now()) {
					store.Client.Del(string(path)).Result()
					store.Client.ZRem(dirListKey, fileName).Result()
					continue
				}
			}
			entries = append(entries, entry)
		}
	}

	return entries, err
}

func genDirectoryListKey(dir string) (dirList string) {
	return dir + DIR_LIST_MARKER
}

func (store *UniversalRedis2Store) Shutdown() {
	store.Client.Close()
}
<commit_msg>remove unused code<commit_after>package redis2

import (
	"context"
	"fmt"
	"time"

	"github.com/go-redis/redis"

	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util"
)

const (
	DIR_LIST_MARKER = "\x00"
)

type UniversalRedis2Store struct {
	Client redis.UniversalClient
	superLargeDirectoryHash map[string]bool
}

func (store *UniversalRedis2Store) isSuperLargeDirectory(dir string) (isSuperLargeDirectory bool) {
	_, isSuperLargeDirectory = store.superLargeDirectoryHash[dir]
	return
}

func (store *UniversalRedis2Store) loadSuperLargeDirectories(superLargeDirectories []string) {
	// set directory hash
	store.superLargeDirectoryHash = make(map[string]bool)
	for _, dir := range superLargeDirectories {
		store.superLargeDirectoryHash[dir] = true
	}
}

func (store *UniversalRedis2Store) BeginTransaction(ctx context.Context) (context.Context, error) {
	return ctx, nil
}
func (store *UniversalRedis2Store) CommitTransaction(ctx context.Context) error {
	return nil
}
func (store *UniversalRedis2Store) RollbackTransaction(ctx context.Context) error {
	return nil
}

func (store *UniversalRedis2Store) InsertEntry(ctx context.Context, entry *filer.Entry) (err error) {

	value, err := entry.EncodeAttributesAndChunks()
	if err != nil {
		return fmt.Errorf("encoding %s %+v: %v", entry.FullPath, entry.Attr, err)
	}

	if len(entry.Chunks) > 50 {
		value = util.MaybeGzipData(value)
	}

	if err = store.Client.Set(string(entry.FullPath), value, time.Duration(entry.TtlSec)*time.Second).Err(); err != nil {
		return fmt.Errorf("persisting %s : %v", entry.FullPath, err)
	}

	dir, name := entry.FullPath.DirAndName()
	if store.isSuperLargeDirectory(dir) {
		return nil
	}

	if name != "" {
		if err = store.Client.ZAddNX(genDirectoryListKey(dir), redis.Z{Score: 0, Member: name}).Err(); err != nil {
			return fmt.Errorf("persisting %s in parent dir: %v", entry.FullPath, err)
		}
	}

	return nil
}

func (store *UniversalRedis2Store) UpdateEntry(ctx context.Context, entry *filer.Entry) (err error) {

	return store.InsertEntry(ctx, entry)
}

func (store *UniversalRedis2Store) FindEntry(ctx context.Context, fullpath util.FullPath) (entry *filer.Entry, err error) {

	data, err := store.Client.Get(string(fullpath)).Result()
	if err == redis.Nil {
		return nil, filer_pb.ErrNotFound
	}

	if err != nil {
		return nil, fmt.Errorf("get %s : %v", fullpath, err)
	}

	entry = &filer.Entry{
		FullPath: fullpath,
	}
	err = entry.DecodeAttributesAndChunks(util.MaybeDecompressData([]byte(data)))
	if err != nil {
		return entry, fmt.Errorf("decode %s : %v", entry.FullPath, err)
	}

	return entry, nil
}

func (store *UniversalRedis2Store) DeleteEntry(ctx context.Context, fullpath util.FullPath) (err error) {

	_, err = store.Client.Del(genDirectoryListKey(string(fullpath))).Result()
	if err != nil {
		return fmt.Errorf("delete dir list %s : %v", fullpath, err)
	}

	_, err = store.Client.Del(string(fullpath)).Result()
	if err != nil {
		return fmt.Errorf("delete %s : %v", fullpath, err)
	}

	dir, name := fullpath.DirAndName()
	if store.isSuperLargeDirectory(dir) {
		return nil
	}
	if name != "" {
		_, err = store.Client.ZRem(genDirectoryListKey(dir), name).Result()
		if err != nil {
			return fmt.Errorf("DeleteEntry %s in parent dir: %v", fullpath, err)
		}
	}

	return nil
}

func (store *UniversalRedis2Store) DeleteFolderChildren(ctx context.Context, fullpath util.FullPath) (err error) {

	if store.isSuperLargeDirectory(string(fullpath)) {
		return nil
	}

	members, err := store.Client.ZRange(genDirectoryListKey(string(fullpath)), 0, -1).Result()
	if err != nil {
		return fmt.Errorf("DeleteFolderChildren %s : %v", fullpath, err)
	}

	for _, fileName := range members {
		path := util.NewFullPath(string(fullpath), fileName)
		_, err = store.Client.Del(string(path)).Result()
		if err != nil {
			return fmt.Errorf("DeleteFolderChildren %s in parent dir: %v", fullpath, err)
		}
	}

	return nil
}

func (store *UniversalRedis2Store) ListDirectoryPrefixedEntries(ctx context.Context, fullpath util.FullPath, startFileName string, inclusive bool, limit int, prefix string) (entries []*filer.Entry, err error) {
	return nil, filer.ErrUnsupportedListDirectoryPrefixed
}

func (store *UniversalRedis2Store) ListDirectoryEntries(ctx context.Context, fullpath util.FullPath, startFileName string, inclusive bool,
	limit int) (entries []*filer.Entry, err error) {

	dirListKey := genDirectoryListKey(string(fullpath))
	start := int64(0)
	if startFileName != "" {
		start, _ = store.Client.ZRank(dirListKey, startFileName).Result()
		if !inclusive {
			start++
		}
	}
	members, err := store.Client.ZRange(dirListKey, start, start+int64(limit)-1).Result()
	if err != nil {
		return nil, fmt.Errorf("list %s : %v", fullpath, err)
	}

	// fetch entry meta
	for _, fileName := range members {
		path := util.NewFullPath(string(fullpath), fileName)
		entry, err := store.FindEntry(ctx, path)
		if err != nil {
			glog.V(0).Infof("list %s : %v", path, err)
		} else {
			if entry.TtlSec > 0 {
				if entry.Attr.Crtime.Add(time.Duration(entry.TtlSec) * time.Second).Before(time.Now()) {
					store.Client.Del(string(path)).Result()
					store.Client.ZRem(dirListKey, fileName).Result()
					continue
				}
			}
			entries = append(entries, entry)
		}
	}

	return entries, err
}

func genDirectoryListKey(dir string) (dirList string) {
	return dir + DIR_LIST_MARKER
}

func (store *UniversalRedis2Store) Shutdown() {
	store.Client.Close()
}
<|endoftext|>


<commit_before>package shell

import (
	"flag"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/pb/remote_pb"
	"github.com/chrislusf/seaweedfs/weed/remote_storage"
	"github.com/chrislusf/seaweedfs/weed/util"
	"io"
	"path/filepath"
	"regexp"
)

func init() {
	Commands = append(Commands, &commandRemoteMountBuckets{})
}

type commandRemoteMountBuckets struct {
}

func (c *commandRemoteMountBuckets) Name() string {
	return "remote.mount.buckets"
}

func (c *commandRemoteMountBuckets) Help() string {
	return `mount all buckets in remote storage and pull its metadata

	# assume a remote storage is configured to name "cloud1"
	remote.configure -name=cloud1 -type=s3 -access_key=xxx -secret_key=yyy

	# mount all buckets
	remote.mount.buckets -remote=cloud1

	# after mount, start a separate process to write updates to remote storage
	weed filer.remote.sync -filer=<filerHost>:<filerPort> -createBucketAt=cloud1

`
}

func (c *commandRemoteMountBuckets) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {

	remoteMountBucketsCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)

	remote := remoteMountBucketsCommand.String("remote", "", "a already configured storage name")
	bucketPattern := remoteMountBucketsCommand.String("bucketPattern", "", "match existing bucket name with wildcard characters '*' and '?'")
	trimBucketSuffix := remoteMountBucketsCommand.Bool("trimBucketSuffix", true, "remote suffix auto generated by 'weed filer.remote.sync'")
	apply := remoteMountBucketsCommand.Bool("apply", false, "apply the mount for listed buckets")

	if err = remoteMountBucketsCommand.Parse(args); err != nil {
		return nil
	}

	if *remote == "" {
		_, err = listExistingRemoteStorageMounts(commandEnv, writer)
		return err
	}

	// find configuration for remote storage
	remoteConf, err := filer.ReadRemoteStorageConf(commandEnv.option.GrpcDialOption, commandEnv.option.FilerAddress, *remote)
	if err != nil {
		return fmt.Errorf("find configuration for %s: %v", *remote, err)
	}

	// get storage client
	remoteStorageClient, err := remote_storage.GetRemoteStorage(remoteConf)
	if err != nil {
		return fmt.Errorf("get storage client for %s: %v", *remote, err)
	}

	buckets, err := remoteStorageClient.ListBuckets()
	if err != nil {
		return fmt.Errorf("list buckets on %s: %v", *remote, err)
	}

	fillerBucketsPath, err := readFilerBucketsPath(commandEnv)
	if err != nil {
		return fmt.Errorf("read filer buckets path: %v", err)
	}

	hasSuffixPattern, _ := regexp.Compile(".+-[0-9][0-9][0-9][0-9]")

	for _, bucket := range buckets {
		if *bucketPattern != "" {
			if matched, _ := filepath.Match(*bucketPattern, bucket.Name); !matched {
				continue
			}
		}

		fmt.Fprintf(writer, "bucket %s\n", bucket.Name)
		localBucketName := bucket.Name
		if *trimBucketSuffix {
			if hasSuffixPattern.MatchString(localBucketName) {
				localBucketName = localBucketName[:len(localBucketName)-5]
				fmt.Fprintf(writer, "  mount bucket %s as %s\n", bucket.Name, localBucketName)
			}
		}
		if *apply {

			dir := util.FullPath(fillerBucketsPath).Child(localBucketName)
			remoteStorageLocation := &remote_pb.RemoteStorageLocation{
				Name:   *remote,
				Bucket: bucket.Name,
				Path:   "/",
			}

			// sync metadata from remote
			if err = syncMetadata(commandEnv, writer, string(dir), true, remoteConf, remoteStorageLocation); err != nil {
				return fmt.Errorf("pull metadata on %+v: %v", remoteStorageLocation, err)
			}

			// store a mount configuration in filer
			if err = filer.InsertMountMapping(commandEnv, string(dir), remoteStorageLocation); err != nil {
				return fmt.Errorf("save mount mapping %s to %+v: %v", dir, remoteStorageLocation, err)
			}

		}
	}

	return nil
}
<commit_msg>minor<commit_after>package shell

import (
	"flag"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/filer"
	"github.com/chrislusf/seaweedfs/weed/pb/remote_pb"
	"github.com/chrislusf/seaweedfs/weed/remote_storage"
	"github.com/chrislusf/seaweedfs/weed/util"
	"io"
	"path/filepath"
	"regexp"
)

func init() {
	Commands = append(Commands, &commandRemoteMountBuckets{})
}

type commandRemoteMountBuckets struct {
}

func (c *commandRemoteMountBuckets) Name() string {
	return "remote.mount.buckets"
}

func (c *commandRemoteMountBuckets) Help() string {
	return `mount all buckets in remote storage and pull its metadata

	# assume a remote storage is configured to name "cloud1"
	remote.configure -name=cloud1 -type=s3 -access_key=xxx -secret_key=yyy

	# mount all buckets
	remote.mount.buckets -remote=cloud1

	# after mount, start a separate process to write updates to remote storage
	weed filer.remote.sync -filer=<filerHost>:<filerPort> -createBucketAt=cloud1

`
}

func (c *commandRemoteMountBuckets) Do(args []string, commandEnv *CommandEnv, writer io.Writer) (err error) {

	remoteMountBucketsCommand := flag.NewFlagSet(c.Name(), flag.ContinueOnError)

	remote := remoteMountBucketsCommand.String("remote", "", "an already configured storage name")
	bucketPattern := remoteMountBucketsCommand.String("bucketPattern", "", "match existing bucket name with wildcard characters '*' and '?'")
	trimBucketSuffix := remoteMountBucketsCommand.Bool("trimBucketSuffix", true, "remote suffix auto generated by 'weed filer.remote.sync'")
	apply := remoteMountBucketsCommand.Bool("apply", false, "apply the mount for listed buckets")

	if err = remoteMountBucketsCommand.Parse(args); err != nil {
		return nil
	}

	if *remote == "" {
		_, err = listExistingRemoteStorageMounts(commandEnv, writer)
		return err
	}

	// find configuration for remote storage
	remoteConf, err := filer.ReadRemoteStorageConf(commandEnv.option.GrpcDialOption, commandEnv.option.FilerAddress, *remote)
	if err != nil {
		return fmt.Errorf("find configuration for %s: %v", *remote, err)
	}

	// get storage client
	remoteStorageClient, err := remote_storage.GetRemoteStorage(remoteConf)
	if err != nil {
		return fmt.Errorf("get storage client for %s: %v", *remote, err)
	}

	buckets, err := remoteStorageClient.ListBuckets()
	if err != nil {
		return fmt.Errorf("list buckets on %s: %v", *remote, err)
	}

	fillerBucketsPath, err := readFilerBucketsPath(commandEnv)
	if err != nil {
		return fmt.Errorf("read filer buckets path: %v", err)
	}

	hasSuffixPattern, _ := regexp.Compile(".+-[0-9][0-9][0-9][0-9]")

	for _, bucket := range buckets {
		if *bucketPattern != "" {
			if matched, _ := filepath.Match(*bucketPattern, bucket.Name); !matched {
				continue
			}
		}

		fmt.Fprintf(writer, "bucket %s\n", bucket.Name)
		localBucketName := bucket.Name
		if *trimBucketSuffix {
			if hasSuffixPattern.MatchString(localBucketName) {
				localBucketName = localBucketName[:len(localBucketName)-5]
				fmt.Fprintf(writer, "  mount bucket %s as %s\n", bucket.Name, localBucketName)
			}
		}
		if *apply {

			dir := util.FullPath(fillerBucketsPath).Child(localBucketName)
			remoteStorageLocation := &remote_pb.RemoteStorageLocation{
				Name:   *remote,
				Bucket: bucket.Name,
				Path:   "/",
			}

			// sync metadata from remote
			if err = syncMetadata(commandEnv, writer, string(dir), true, remoteConf, remoteStorageLocation); err != nil {
				return fmt.Errorf("pull metadata on %+v: %v", remoteStorageLocation, err)
			}

			// store a mount configuration in filer
			if err = filer.InsertMountMapping(commandEnv, string(dir), remoteStorageLocation); err != nil {
				return fmt.Errorf("save mount mapping %s to %+v: %v", dir, remoteStorageLocation, err)
			}

		}
	}

	return nil
}
<|endoftext|>


<commit_before>package noaa_test

import (
	"bytes"
	"crypto/tls"
	"errors"
	"github.com/cloudfoundry/noaa"
	"github.com/cloudfoundry/loggregatorlib/server/handlers"
	"github.com/cloudfoundry/loggregatorlib/loggertesthelper"
	"github.com/elazarl/goproxy"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"time"
	"github.com/cloudfoundry/dropsonde/events"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Noaa behind a Proxy", func() {
	var (
		connection        noaa.Noaa
		endpoint          string
		testServer        *httptest.Server
		tlsSettings       *tls.Config
		consumerProxyFunc func(*http.Request) (*url.URL, error)

		appGuid         string
		authToken       string
		incomingChan    <-chan *events.Envelope
		messagesToSend  chan []byte
		testProxyServer *httptest.Server
		goProxyHandler  *goproxy.ProxyHttpServer

		err error
	)

	BeforeEach(func() {
		messagesToSend = make(chan []byte, 256)

		testServer = httptest.NewServer(handlers.NewWebsocketHandler(messagesToSend, 100*time.Millisecond, loggertesthelper.Logger()))
		endpoint = "ws://" + testServer.Listener.Addr().String()
		goProxyHandler = goproxy.NewProxyHttpServer()
		goProxyHandler.Logger = log.New(bytes.NewBufferString(""), "", 0)
		testProxyServer = httptest.NewServer(goProxyHandler)
		consumerProxyFunc = func(*http.Request) (*url.URL, error) {
			return url.Parse(testProxyServer.URL)
		}
	})

	AfterEach(func() {
		consumerProxyFunc = nil
		if testProxyServer != nil {
			testProxyServer.Close()
		}
		if testServer != nil {
			testServer.Close()
		}
	})

	Describe("Stream", func() {

		AfterEach(func() {
			close(messagesToSend)
		})

		perform := func() {
			connection = noaa.NewNoaa(endpoint, tlsSettings, consumerProxyFunc)
			incomingChan, err = connection.Stream(appGuid, authToken)
		}

		It("connects using valid URL to running consumerProxyFunc server", func() {
			messagesToSend <- marshalMessage(createMessage("hello", 0))
			perform()

			message := <-incomingChan

			Expect(message.GetLogMessage().GetMessage()).To(Equal([]byte("hello")))
		})

		It("connects using valid URL to a stopped consumerProxyFunc server", func() {
			testProxyServer.Close()

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("connection refused"))
		})

		It("connects using invalid URL", func() {
			errMsg := "Invalid consumerProxyFunc URL"
			consumerProxyFunc = func(*http.Request) (*url.URL, error) {
				return nil, errors.New(errMsg)
			}

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring(errMsg))
		})

		It("connects to a consumerProxyFunc server rejecting CONNECT requests", func() {
			goProxyHandler.OnRequest().HandleConnect(goproxy.AlwaysReject)

			perform()

			Expect(err).To(HaveOccurred())
		})

		It("connects to a non-consumerProxyFunc server", func() {
			nonProxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				http.Error(w, "Go away, I am not a consumerProxyFunc!", http.StatusBadRequest)
			}))
			consumerProxyFunc = func(*http.Request) (*url.URL, error) {
				return url.Parse(nonProxyServer.URL)
			}

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring(http.StatusText(http.StatusBadRequest)))
		})
	})

	Describe("RecentLogs", func() {
		var httpTestServer *httptest.Server
		var incomingMessages []*events.Envelope

		perform := func() {
			close(messagesToSend)
			connection = noaa.NewNoaa(endpoint, tlsSettings, consumerProxyFunc)
			incomingMessages, err = connection.RecentLogs(appGuid, authToken)
		}

		BeforeEach(func() {
			httpTestServer = httptest.NewServer(handlers.NewHttpHandler(messagesToSend, loggertesthelper.Logger()))
			endpoint = "ws://" + httpTestServer.Listener.Addr().String()
		})

		AfterEach(func() {
			httpTestServer.Close()
		})

		It("returns messages from the server", func() {
			messagesToSend <- marshalMessage(createMessage("test-message-0", 0))
			messagesToSend <- marshalMessage(createMessage("test-message-1", 0))

			perform()

			Expect(err).NotTo(HaveOccurred())
			Expect(incomingMessages).To(HaveLen(2))
			Expect(incomingMessages[0].GetLogMessage().GetMessage()).To(Equal([]byte("test-message-0")))
			Expect(incomingMessages[1].GetLogMessage().GetMessage()).To(Equal([]byte("test-message-1")))
		})

		FIt("connects using failing proxyFunc", func() {
			errMsg := "Invalid consumerProxyFunc URL"
			consumerProxyFunc = func(*http.Request) (*url.URL, error) {
				return nil, errors.New(errMsg)
			}

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring(errMsg))
		})
	})
})
<commit_msg>Removing focus on test.<commit_after>package noaa_test

import (
	"bytes"
	"crypto/tls"
	"errors"
	"github.com/cloudfoundry/noaa"
	"github.com/cloudfoundry/loggregatorlib/server/handlers"
	"github.com/cloudfoundry/loggregatorlib/loggertesthelper"
	"github.com/elazarl/goproxy"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"time"
	"github.com/cloudfoundry/dropsonde/events"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Noaa behind a Proxy", func() {
	var (
		connection        noaa.Noaa
		endpoint          string
		testServer        *httptest.Server
		tlsSettings       *tls.Config
		consumerProxyFunc func(*http.Request) (*url.URL, error)

		appGuid         string
		authToken       string
		incomingChan    <-chan *events.Envelope
		messagesToSend  chan []byte
		testProxyServer *httptest.Server
		goProxyHandler  *goproxy.ProxyHttpServer

		err error
	)

	BeforeEach(func() {
		messagesToSend = make(chan []byte, 256)

		testServer = httptest.NewServer(handlers.NewWebsocketHandler(messagesToSend, 100*time.Millisecond, loggertesthelper.Logger()))
		endpoint = "ws://" + testServer.Listener.Addr().String()
		goProxyHandler = goproxy.NewProxyHttpServer()
		goProxyHandler.Logger = log.New(bytes.NewBufferString(""), "", 0)
		testProxyServer = httptest.NewServer(goProxyHandler)
		consumerProxyFunc = func(*http.Request) (*url.URL, error) {
			return url.Parse(testProxyServer.URL)
		}
	})

	AfterEach(func() {
		consumerProxyFunc = nil
		if testProxyServer != nil {
			testProxyServer.Close()
		}
		if testServer != nil {
			testServer.Close()
		}
	})

	Describe("Stream", func() {

		AfterEach(func() {
			close(messagesToSend)
		})

		perform := func() {
			connection = noaa.NewNoaa(endpoint, tlsSettings, consumerProxyFunc)
			incomingChan, err = connection.Stream(appGuid, authToken)
		}

		It("connects using valid URL to running consumerProxyFunc server", func() {
			messagesToSend <- marshalMessage(createMessage("hello", 0))
			perform()

			message := <-incomingChan

			Expect(message.GetLogMessage().GetMessage()).To(Equal([]byte("hello")))
		})

		It("connects using valid URL to a stopped consumerProxyFunc server", func() {
			testProxyServer.Close()

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring("connection refused"))
		})

		It("connects using invalid URL", func() {
			errMsg := "Invalid consumerProxyFunc URL"
			consumerProxyFunc = func(*http.Request) (*url.URL, error) {
				return nil, errors.New(errMsg)
			}

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring(errMsg))
		})

		It("connects to a consumerProxyFunc server rejecting CONNECT requests", func() {
			goProxyHandler.OnRequest().HandleConnect(goproxy.AlwaysReject)

			perform()

			Expect(err).To(HaveOccurred())
		})

		It("connects to a non-consumerProxyFunc server", func() {
			nonProxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
				http.Error(w, "Go away, I am not a consumerProxyFunc!", http.StatusBadRequest)
			}))
			consumerProxyFunc = func(*http.Request) (*url.URL, error) {
				return url.Parse(nonProxyServer.URL)
			}

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring(http.StatusText(http.StatusBadRequest)))
		})
	})

	Describe("RecentLogs", func() {
		var httpTestServer *httptest.Server
		var incomingMessages []*events.Envelope

		perform := func() {
			close(messagesToSend)
			connection = noaa.NewNoaa(endpoint, tlsSettings, consumerProxyFunc)
			incomingMessages, err = connection.RecentLogs(appGuid, authToken)
		}

		BeforeEach(func() {
			httpTestServer = httptest.NewServer(handlers.NewHttpHandler(messagesToSend, loggertesthelper.Logger()))
			endpoint = "ws://" + httpTestServer.Listener.Addr().String()
		})

		AfterEach(func() {
			httpTestServer.Close()
		})

		It("returns messages from the server", func() {
			messagesToSend <- marshalMessage(createMessage("test-message-0", 0))
			messagesToSend <- marshalMessage(createMessage("test-message-1", 0))

			perform()

			Expect(err).NotTo(HaveOccurred())
			Expect(incomingMessages).To(HaveLen(2))
			Expect(incomingMessages[0].GetLogMessage().GetMessage()).To(Equal([]byte("test-message-0")))
			Expect(incomingMessages[1].GetLogMessage().GetMessage()).To(Equal([]byte("test-message-1")))
		})

		It("connects using failing proxyFunc", func() {
			errMsg := "Invalid consumerProxyFunc URL"
			consumerProxyFunc = func(*http.Request) (*url.URL, error) {
				return nil, errors.New(errMsg)
			}

			perform()

			Expect(err).To(HaveOccurred())
			Expect(err.Error()).To(ContainSubstring(errMsg))
		})
	})
})
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

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.
*/

// podex is a command line tool to bootstrap kubernetes container
// manifests from docker image metadata.
//
// Manifests can then be edited by a human to match deployment needs.
//
// Example usage:
//
// $ docker pull google/nodejs-hello
// $ podex -yaml google/nodejs-hello > google/nodejs-hello/pod.yaml
// $ podex -json google/nodejs-hello > google/nodejs-hello/pod.json

package main

import (
	"encoding/json"
	"flag"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
	dockerclient "github.com/fsouza/go-dockerclient"
	"gopkg.in/v1/yaml"
)

const usage = "usage: podex [-json|-yaml] <repo/dockerimage>"

var generateJSON = flag.Bool("json", false, "generate json manifest")
var generateYAML = flag.Bool("yaml", false, "generate yaml manifest")

func main() {
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatal(usage)
	}

	imageName := flag.Arg(0)
	if len(imageName) == 0 {
		log.Fatal(usage)
	}

	if (!*generateJSON && !*generateYAML) || (*generateJSON && *generateYAML) {
		log.Fatal(usage)
	}

	parts := strings.Split(imageName, "/")
	baseName := parts[len(parts)-1]

	dockerHost := os.Getenv("DOCKER_HOST")
	docker, err := dockerclient.NewClient(dockerHost)
	if err != nil {
		log.Fatalf("failed to connect to %q: %v", dockerHost, err)
	}

	// TODO(proppy): use the regitry API instead of the remote API to get image metadata.
	img, err := docker.InspectImage(imageName)
	if err != nil {
		log.Fatalf("failed to inspect image %q: %v", imageName, err)
	}
	// TODO(proppy): add flag to handle multiple version
	manifest := v1beta1.ContainerManifest{
		Version: "v1beta1",
		ID:      baseName + "-pod",
		Containers: []v1beta1.Container{{
			Name:  baseName,
			Image: imageName,
		}},
		RestartPolicy: v1beta1.RestartPolicy{
			Always: &v1beta1.RestartPolicyAlways{},
		},
	}
	for p, _ := range img.Config.ExposedPorts {
		port, err := strconv.Atoi(p.Port())
		if err != nil {
			log.Fatalf("failed to parse port %q: %v", parts[0], err)
		}
		manifest.Containers[0].Ports = append(manifest.Containers[0].Ports, v1beta1.Port{
			Name:          strings.Join([]string{baseName, p.Proto(), p.Port()}, "-"),
			ContainerPort: port,
			Protocol:      strings.ToUpper(p.Proto()),
		})
	}
	if *generateJSON {
		bs, err := json.MarshalIndent(manifest, "", "  ")
		if err != nil {
			log.Fatalf("failed to render JSON container manifest: %v", err)
		}
		os.Stdout.Write(bs)
	}
	if *generateYAML {
		bs, err := yaml.Marshal(manifest)
		if err != nil {
			log.Fatalf("failed to render YAML container manifest: %v", err)
		}
		os.Stdout.Write(bs)
	}
}
<commit_msg>podex: add comment about image name parsing<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

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.
*/

// podex is a command line tool to bootstrap kubernetes container
// manifests from docker image metadata.
//
// Manifests can then be edited by a human to match deployment needs.
//
// Example usage:
//
// $ docker pull google/nodejs-hello
// $ podex -yaml google/nodejs-hello > google/nodejs-hello/pod.yaml
// $ podex -json google/nodejs-hello > google/nodejs-hello/pod.json

package main

import (
	"encoding/json"
	"flag"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
	dockerclient "github.com/fsouza/go-dockerclient"
	"gopkg.in/v1/yaml"
)

const usage = "usage: podex [-json|-yaml] <repo/dockerimage>"

var generateJSON = flag.Bool("json", false, "generate json manifest")
var generateYAML = flag.Bool("yaml", false, "generate yaml manifest")

func main() {
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatal(usage)
	}

	imageName := flag.Arg(0)
	if len(imageName) == 0 {
		log.Fatal(usage)
	}

	if (!*generateJSON && !*generateYAML) || (*generateJSON && *generateYAML) {
		log.Fatal(usage)
	}

	// Parse docker image name
	// IMAGE: [REGISTRYHOST/][USERNAME/]NAME[:TAG]
	// NAME: [a-z0-9-_.]
	parts := strings.Split(imageName, "/")
	baseName := parts[len(parts)-1]

	dockerHost := os.Getenv("DOCKER_HOST")
	docker, err := dockerclient.NewClient(dockerHost)
	if err != nil {
		log.Fatalf("failed to connect to %q: %v", dockerHost, err)
	}

	// TODO(proppy): use the regitry API instead of the remote API to get image metadata.
	img, err := docker.InspectImage(imageName)
	if err != nil {
		log.Fatalf("failed to inspect image %q: %v", imageName, err)
	}
	// TODO(proppy): add flag to handle multiple version
	manifest := v1beta1.ContainerManifest{
		Version: "v1beta1",
		ID:      baseName + "-pod",
		Containers: []v1beta1.Container{{
			Name:  baseName,
			Image: imageName,
		}},
		RestartPolicy: v1beta1.RestartPolicy{
			Always: &v1beta1.RestartPolicyAlways{},
		},
	}
	for p, _ := range img.Config.ExposedPorts {
		port, err := strconv.Atoi(p.Port())
		if err != nil {
			log.Fatalf("failed to parse port %q: %v", parts[0], err)
		}
		manifest.Containers[0].Ports = append(manifest.Containers[0].Ports, v1beta1.Port{
			Name:          strings.Join([]string{baseName, p.Proto(), p.Port()}, "-"),
			ContainerPort: port,
			Protocol:      strings.ToUpper(p.Proto()),
		})
	}
	if *generateJSON {
		bs, err := json.MarshalIndent(manifest, "", "  ")
		if err != nil {
			log.Fatalf("failed to render JSON container manifest: %v", err)
		}
		os.Stdout.Write(bs)
	}
	if *generateYAML {
		bs, err := yaml.Marshal(manifest)
		if err != nil {
			log.Fatalf("failed to render YAML container manifest: %v", err)
		}
		os.Stdout.Write(bs)
	}
}
<|endoftext|>


<commit_before>package stack_test

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/appcelerator/amp/api/rpc/stack"
	"github.com/appcelerator/amp/api/runtime"
	"github.com/appcelerator/amp/api/server"
	"github.com/appcelerator/amp/api/state"
	"github.com/docker/docker/pkg/stringid"
	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
)

const (
	example1 = `
pinger:
  image: appcelerator/pinger
  replicas: 2
pingerExt1:
  image: appcelerator/pinger
  replicas: 2
  public:
    - name: www1
      protocol: tcp
      internal_port: 3000
pingerExt2:
  image: appcelerator/pinger
  replicas: 2
  public:
    - name: www2
      protocol: tcp
      publish_port: 3001
      internal_port: 3000`
	example2 = `
services:
  pinger:
    image: appcelerator/pinger
    replicas: 2
  pingerExt1:
    image: appcelerator/pinger
    replicas: 2
    public:
      - name: www1
        protocol: tcp
        internal_port: 3000
  pingerExt2:
    image: appcelerator/pinger
    replicas: 2
    public:
      - name: www2
        protocol: tcp
        publish_port: 3002
        internal_port: 3000`
)

var (
	client stack.StackServiceClient
	ctx    context.Context
)

func TestMain(m *testing.M) {
	_, conn := server.StartTestServer()
	ctx = context.Background()
	client = stack.NewStackServiceClient(conn)
	os.Exit(m.Run())
}

//Test two stacks life cycle in the same time
func TestShouldManageStackLifeCycleSuccessfully(t *testing.T) {
	//Start stack essai1
	name1 := fmt.Sprintf("test1-%d", time.Now().Unix())
	name2 := fmt.Sprintf("test2-%d", time.Now().Unix())
	//Start stack test1
	t.Log("start stack " + name1)
	rUp1, errUp1 := client.Up(ctx, &stack.UpRequest{StackName: name1, Stackfile: example1})
	if errUp1 != nil {
		t.Fatal(errUp1)
	}
	//Start stack test2
	t.Log("start stack " + name2)
	rUp2, errUp2 := client.Up(ctx, &stack.UpRequest{StackName: name2, Stackfile: example2})
	if errUp2 != nil {
		t.Fatal(errUp2)
	}
	assert.NotEmpty(t, rUp1.StackId, "Stack test1 StackId should not be empty")
	assert.NotEmpty(t, rUp2.StackId, "Stack test2 StackId should not be empty")
	time.Sleep(3 * time.Second)
	//verifyusing ls
	t.Log("perform stack ls")
	listRequest := stack.ListRequest{}
	_, errls := client.List(ctx, &listRequest)
	if errls != nil {
		t.Fatal(errls)
	}
	//Prepare requests
	stackRequest1 := stack.StackRequest{
		StackIdent: rUp1.StackId,
	}
	stackRequest2 := stack.StackRequest{
		StackIdent: rUp2.StackId,
	}
	//Stop stack test1
	t.Log("stop stack " + name1)
	rStop1, errStop1 := client.Stop(ctx, &stackRequest1)
	if errStop1 != nil {
		t.Fatal(errStop1)
	}
	assert.NotEmpty(t, rStop1.StackId, "Stack test1 StackId should not be empty")
	//Restart stack test1
	time.Sleep(1 * time.Second)
	t.Log("restart stack " + name1)
	rRestart1, errRestart1 := client.Start(ctx, &stackRequest1)
	if errRestart1 != nil {
		t.Fatal(errRestart1)
	}
	assert.NotEmpty(t, rRestart1.StackId, "Stack test1 StackId should not be empty")
	time.Sleep(1 * time.Second)
	//Stop again stack test1
	t.Log("stop again stack " + name1)
	rStop12, errStop12 := client.Stop(ctx, &stackRequest1)
	if errStop12 != nil {
		t.Fatal(errStop12)
	}
	assert.NotEmpty(t, rStop12.StackId, "Stack test1 StackId should not be empty")
	t.Log("remove stack " + name1)
	//Remove stack test1
	removeRequest1 := stack.RemoveRequest{
		StackIdent: rUp1.StackId,
		Force:      false,
	}
	rRemove1, errRemove1 := client.Remove(ctx, &removeRequest1)
	if errRemove1 != nil {
		t.Fatal(errRemove1)
	}
	assert.NotEmpty(t, rRemove1.StackId, "Stack test1 StackId should not be empty")
	//Stop stack test2
	t.Log("stop stack " + name2)
	rStop2, errStop2 := client.Stop(ctx, &stackRequest2)
	if errStop2 != nil {
		t.Fatal(errStop2)
	}
	assert.NotEmpty(t, rStop2.StackId, "Stack test2 StackId should not be empty")
	//Remove stack test2
	t.Log("remove stack " + name2)
	removeRequest2 := stack.RemoveRequest{
		StackIdent: rUp2.StackId,
		Force:      false,
	}
	rRemove2, errRemove2 := client.Remove(ctx, &removeRequest2)
	if errRemove2 != nil {
		t.Fatal(errRemove2)
	}
	assert.NotEmpty(t, rRemove2.StackId, "Stack test2 StackId should not be empty")
}

func TestTransitionsFromStopped(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)

	id := stringid.GenerateNonCryptoID()
	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}

func TestTransitionsFromStarting(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)
	id := stringid.GenerateNonCryptoID()

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}

func TestTransitionsFromRunning(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)
	id := stringid.GenerateNonCryptoID()

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}

func TestTransitionsFromRedeploying(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)
	id := stringid.GenerateNonCryptoID()

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}
<commit_msg>fix stack test (#333)<commit_after>package stack_test

import (
	"fmt"
	"os"
	"testing"
	"time"

	"github.com/appcelerator/amp/api/rpc/stack"
	"github.com/appcelerator/amp/api/runtime"
	"github.com/appcelerator/amp/api/server"
	"github.com/appcelerator/amp/api/state"
	"github.com/docker/docker/pkg/stringid"
	"github.com/stretchr/testify/assert"
	"golang.org/x/net/context"
)

const (
	example1 = `
services:
  pinger:
    image: appcelerator/pinger
    replicas: 2
  pingerExt1:
    image: appcelerator/pinger
    replicas: 2
    public:
      - name: www1
        protocol: tcp
        internal_port: 3000
  pingerExt2:
    image: appcelerator/pinger
    replicas: 2
    public:
      - name: www2
        protocol: tcp
        publish_port: 3001
        internal_port: 3000`
	example2 = `
services:
  pinger:
    image: appcelerator/pinger
    replicas: 2
  pingerExt1:
    image: appcelerator/pinger
    replicas: 2
    public:
      - name: www1
        protocol: tcp
        internal_port: 3000
  pingerExt2:
    image: appcelerator/pinger
    replicas: 2
    public:
      - name: www2
        protocol: tcp
        publish_port: 3002
        internal_port: 3000`
)

var (
	client stack.StackServiceClient
	ctx    context.Context
)

func TestMain(m *testing.M) {
	_, conn := server.StartTestServer()
	ctx = context.Background()
	client = stack.NewStackServiceClient(conn)
	os.Exit(m.Run())
}

//Test two stacks life cycle in the same time
func TestShouldManageStackLifeCycleSuccessfully(t *testing.T) {
	//Start stack essai1
	name1 := fmt.Sprintf("test1-%d", time.Now().Unix())
	name2 := fmt.Sprintf("test2-%d", time.Now().Unix())
	//Start stack test1
	t.Log("start stack " + name1)
	rUp1, errUp1 := client.Up(ctx, &stack.UpRequest{StackName: name1, Stackfile: example1})
	if errUp1 != nil {
		t.Fatal(errUp1)
	}
	//Start stack test2
	t.Log("start stack " + name2)
	rUp2, errUp2 := client.Up(ctx, &stack.UpRequest{StackName: name2, Stackfile: example2})
	if errUp2 != nil {
		t.Fatal(errUp2)
	}
	assert.NotEmpty(t, rUp1.StackId, "Stack test1 StackId should not be empty")
	assert.NotEmpty(t, rUp2.StackId, "Stack test2 StackId should not be empty")
	time.Sleep(3 * time.Second)
	//verifyusing ls
	t.Log("perform stack ls")
	listRequest := stack.ListRequest{}
	_, errls := client.List(ctx, &listRequest)
	if errls != nil {
		t.Fatal(errls)
	}
	//Prepare requests
	stackRequest1 := stack.StackRequest{
		StackIdent: rUp1.StackId,
	}
	stackRequest2 := stack.StackRequest{
		StackIdent: rUp2.StackId,
	}
	//Stop stack test1
	t.Log("stop stack " + name1)
	rStop1, errStop1 := client.Stop(ctx, &stackRequest1)
	if errStop1 != nil {
		t.Fatal(errStop1)
	}
	assert.NotEmpty(t, rStop1.StackId, "Stack test1 StackId should not be empty")
	//Restart stack test1
	time.Sleep(1 * time.Second)
	t.Log("restart stack " + name1)
	rRestart1, errRestart1 := client.Start(ctx, &stackRequest1)
	if errRestart1 != nil {
		t.Fatal(errRestart1)
	}
	assert.NotEmpty(t, rRestart1.StackId, "Stack test1 StackId should not be empty")
	time.Sleep(1 * time.Second)
	//Stop again stack test1
	t.Log("stop again stack " + name1)
	rStop12, errStop12 := client.Stop(ctx, &stackRequest1)
	if errStop12 != nil {
		t.Fatal(errStop12)
	}
	assert.NotEmpty(t, rStop12.StackId, "Stack test1 StackId should not be empty")
	t.Log("remove stack " + name1)
	//Remove stack test1
	removeRequest1 := stack.RemoveRequest{
		StackIdent: rUp1.StackId,
		Force:      false,
	}
	rRemove1, errRemove1 := client.Remove(ctx, &removeRequest1)
	if errRemove1 != nil {
		t.Fatal(errRemove1)
	}
	assert.NotEmpty(t, rRemove1.StackId, "Stack test1 StackId should not be empty")
	//Stop stack test2
	t.Log("stop stack " + name2)
	rStop2, errStop2 := client.Stop(ctx, &stackRequest2)
	if errStop2 != nil {
		t.Fatal(errStop2)
	}
	assert.NotEmpty(t, rStop2.StackId, "Stack test2 StackId should not be empty")
	//Remove stack test2
	t.Log("remove stack " + name2)
	removeRequest2 := stack.RemoveRequest{
		StackIdent: rUp2.StackId,
		Force:      false,
	}
	rRemove2, errRemove2 := client.Remove(ctx, &removeRequest2)
	if errRemove2 != nil {
		t.Fatal(errRemove2)
	}
	assert.NotEmpty(t, rRemove2.StackId, "Stack test2 StackId should not be empty")
}

func TestTransitionsFromStopped(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)

	id := stringid.GenerateNonCryptoID()
	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Stopped))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}

func TestTransitionsFromStarting(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)
	id := stringid.GenerateNonCryptoID()

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Starting))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}

func TestTransitionsFromRunning(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)
	id := stringid.GenerateNonCryptoID()

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Running))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}

func TestTransitionsFromRedeploying(t *testing.T) {
	machine := state.NewMachine(stack.StackRuleSet, runtime.Store)
	id := stringid.GenerateNonCryptoID()

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Stopped)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.NoError(t, machine.TransitionTo(id, int32(stack.StackState_Starting)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Running)))
	machine.DeleteState(id)

	machine.CreateState(id, int32(stack.StackState_Redeploying))
	assert.Error(t, machine.TransitionTo(id, int32(stack.StackState_Redeploying)))
	machine.DeleteState(id)
}
<|endoftext|>


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

package apiserver

import (
	"github.com/juju/errors"
	"github.com/juju/utils/set"

	"github.com/juju/juju/rpc/rpcreflect"
	"github.com/juju/juju/state"
)

var inUpgradeError = errors.New("upgrade in progress - Juju functionality is limited")

type upgradingRoot struct {
	srvRoot
}

var _ apiRoot = (*upgradingRoot)(nil)

// newUpgradingRoot creates a root where all but a few "safe" API
// calls fail with inUpgradeError.
func newUpgradingRoot(root *initialRoot, entity state.Entity) *upgradingRoot {
	return &upgradingRoot{
		srvRoot: *newSrvRoot(root, entity),
	}
}

// FindMethod extended srvRoot.FindMethod. It returns inUpgradeError
// for most API calls except those that are deemed safe or important
// for use while Juju is upgrading.
func (r *upgradingRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) {
	if _, _, err := r.lookupMethod(rootName, version, methodName); err != nil {
		return nil, err
	}
	if !isMethodAllowedDuringUpgrade(rootName, methodName) {
		return nil, inUpgradeError
	}
	return r.srvRoot.FindMethod(rootName, version, methodName)
}

var allowedMethodsDuringUpgrades = set.NewStrings(
	"Client.FullStatus",     // for "juju status"
	"Client.PrivateAddress", // for "juju ssh"
	"Client.PublicAddress",  // for "juju ssh"
	"Client.WatchDebugLog",  // for "juju debug-log"
)

func isMethodAllowedDuringUpgrade(rootName, methodName string) bool {
	fullName := rootName + "." + methodName
	return allowedMethodsDuringUpgrades.Contains(fullName)
}
<commit_msg>apiserver: allow an extra API method during upgrades<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package apiserver

import (
	"github.com/juju/errors"
	"github.com/juju/utils/set"

	"github.com/juju/juju/rpc/rpcreflect"
	"github.com/juju/juju/state"
)

var inUpgradeError = errors.New("upgrade in progress - Juju functionality is limited")

type upgradingRoot struct {
	srvRoot
}

var _ apiRoot = (*upgradingRoot)(nil)

// newUpgradingRoot creates a root where all but a few "safe" API
// calls fail with inUpgradeError.
func newUpgradingRoot(root *initialRoot, entity state.Entity) *upgradingRoot {
	return &upgradingRoot{
		srvRoot: *newSrvRoot(root, entity),
	}
}

// FindMethod extended srvRoot.FindMethod. It returns inUpgradeError
// for most API calls except those that are deemed safe or important
// for use while Juju is upgrading.
func (r *upgradingRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) {
	if _, _, err := r.lookupMethod(rootName, version, methodName); err != nil {
		return nil, err
	}
	if !isMethodAllowedDuringUpgrade(rootName, methodName) {
		return nil, inUpgradeError
	}
	return r.srvRoot.FindMethod(rootName, version, methodName)
}

var allowedMethodsDuringUpgrades = set.NewStrings(
	"Client.FullStatus",     // for "juju status"
	"Client.EnvironmentGet", // for "juju ssh"
	"Client.PrivateAddress", // for "juju ssh"
	"Client.PublicAddress",  // for "juju ssh"
	"Client.WatchDebugLog",  // for "juju debug-log"
)

func isMethodAllowedDuringUpgrade(rootName, methodName string) bool {
	fullName := rootName + "." + methodName
	return allowedMethodsDuringUpgrades.Contains(fullName)
}
<|endoftext|>