Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added better support for columnar provisioning on docker. #62

Merged
merged 1 commit into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions clusterdef/fromshortstring.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ func FromShortString(shortStr string) (*Cluster, error) {
FtsMemoryMB: 1024,
},
}, nil
} else if defName == "columnar" {
return &Cluster{
Columnar: true,
NodeGroups: []*NodeGroup{
{
Count: 3,
Version: defVersion,
},
},
}, nil
} else if defName == "columnar-single" {
return &Cluster{
Columnar: true,
NodeGroups: []*NodeGroup{
{
Count: 1,
Version: defVersion,
},
},
}, nil
}

return nil, fmt.Errorf("unknown short string name `%s`", defName)
Expand Down
32 changes: 21 additions & 11 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ type ClusterListOutput []ClusterListOutput_Item

type ClusterListOutput_Item struct {
ID string `json:"id"`
Type string `json:"type"`
State string `json:"state"`
Expiry *time.Time `json:"expiry,omitempty"`
Deployer string `json:"deployer"`
Nodes []ClusterListOutput_Node `json:"nodes"`
}

type ClusterListOutput_Node struct {
ID string `json:"id"`
Name string `json:"name"`
IPAddress string `json:"ip_address"`
ResourceID string `json:"resource_id"`
ID string `json:"id"`
Name string `json:"name"`
IPAddress string `json:"ip_address"`
ResourceID string `json:"resource_id"`
IsClusterNode bool `json:"is_cluster_node"`
}

var listCmd = &cobra.Command{
Expand Down Expand Up @@ -88,14 +90,20 @@ var listCmd = &cobra.Command{
expiryStr = time.Until(cluster.GetExpiry()).Round(time.Second).String()
}

fmt.Printf(" %s [State: %s, Timeout: %s, Deployer: %s]\n",
fmt.Printf(" %s [Type: %s, State: %s, Timeout: %s, Deployer: %s]\n",
cluster.GetID(),
cluster.GetType(),
cluster.GetState(),
expiryStr,
deployerName)
for _, node := range cluster.GetNodes() {
fmt.Printf(" %-16s %-20s %-20s %s\n",
node.GetID(),
printId := node.GetID()
if !node.IsClusterNode() {
printId = "[UTIL] " + printId
}

fmt.Printf(" %-40s %-20s %-20s %s\n",
printId,
node.GetName(),
node.GetIPAddress(),
node.GetResourceID())
Expand All @@ -106,6 +114,7 @@ var listCmd = &cobra.Command{
for _, cluster := range clusters {
clusterItem := ClusterListOutput_Item{
ID: cluster.Info.GetID(),
Type: string(cluster.Info.GetType()),
State: cluster.Info.GetState(),
Deployer: cluster.DeployerName,
}
Expand All @@ -117,10 +126,11 @@ var listCmd = &cobra.Command{

for _, node := range cluster.Info.GetNodes() {
clusterItem.Nodes = append(clusterItem.Nodes, ClusterListOutput_Node{
ID: node.GetID(),
Name: node.GetName(),
IPAddress: node.GetIPAddress(),
ResourceID: node.GetResourceID(),
ID: node.GetID(),
Name: node.GetName(),
IPAddress: node.GetIPAddress(),
ResourceID: node.GetResourceID(),
IsClusterNode: node.IsClusterNode(),
})
}
out = append(out, clusterItem)
Expand Down
9 changes: 5 additions & 4 deletions deployment/caodeploy/clusterinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ type ClusterInfo struct {

var _ (deployment.ClusterInfo) = (*ClusterInfo)(nil)

func (i ClusterInfo) GetID() string { return i.ClusterID }
func (i ClusterInfo) GetPurpose() string { return "" }
func (i ClusterInfo) GetExpiry() time.Time { return i.Expiry }
func (i ClusterInfo) GetState() string { return i.State }
func (i ClusterInfo) GetID() string { return i.ClusterID }
func (i ClusterInfo) GetType() deployment.ClusterType { return deployment.ClusterTypeServer }
func (i ClusterInfo) GetPurpose() string { return "" }
func (i ClusterInfo) GetExpiry() time.Time { return i.Expiry }
func (i ClusterInfo) GetState() string { return i.State }
func (i ClusterInfo) GetNodes() []deployment.ClusterNodeInfo {
return nil
}
9 changes: 5 additions & 4 deletions deployment/clouddeploy/clusterinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ type ClusterInfo struct {

var _ (deployment.ClusterInfo) = (*ClusterInfo)(nil)

func (i ClusterInfo) GetID() string { return i.ClusterID }
func (i ClusterInfo) GetPurpose() string { return "" }
func (i ClusterInfo) GetExpiry() time.Time { return i.Expiry }
func (i ClusterInfo) GetState() string { return i.State }
func (i ClusterInfo) GetID() string { return i.ClusterID }
func (i ClusterInfo) GetType() deployment.ClusterType { return deployment.ClusterTypeServer }
func (i ClusterInfo) GetPurpose() string { return "" }
func (i ClusterInfo) GetExpiry() time.Time { return i.Expiry }
func (i ClusterInfo) GetState() string { return i.State }
func (i ClusterInfo) GetNodes() []deployment.ClusterNodeInfo {
return nil
}
9 changes: 9 additions & 0 deletions deployment/deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@ import (
"github.com/couchbaselabs/cbdinocluster/clusterdef"
)

type ClusterType string

const (
ClusterTypeServer ClusterType = "server"
ClusterTypeColumnar ClusterType = "columnar"
)

type ClusterNodeInfo interface {
GetID() string
IsClusterNode() bool
GetResourceID() string
GetName() string
GetIPAddress() string
}

type ClusterInfo interface {
GetID() string
GetType() ClusterType
GetPurpose() string
GetExpiry() time.Time
GetState() string
Expand Down
12 changes: 8 additions & 4 deletions deployment/dockerdeploy/clusterinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

type ClusterNodeInfo struct {
NodeID string
IsNode bool
Name string
ResourceID string
IPAddress string
Expand All @@ -16,12 +17,14 @@ type ClusterNodeInfo struct {
var _ (deployment.ClusterNodeInfo) = (*ClusterNodeInfo)(nil)

func (i ClusterNodeInfo) GetID() string { return i.NodeID }
func (i ClusterNodeInfo) IsClusterNode() bool { return i.IsNode }
func (i ClusterNodeInfo) GetName() string { return i.Name }
func (i ClusterNodeInfo) GetResourceID() string { return i.ResourceID }
func (i ClusterNodeInfo) GetIPAddress() string { return i.IPAddress }

type ClusterInfo struct {
ClusterID string
Type deployment.ClusterType
Creator string
Owner string
Purpose string
Expand All @@ -31,10 +34,11 @@ type ClusterInfo struct {

var _ (deployment.ClusterInfo) = (*ClusterInfo)(nil)

func (i ClusterInfo) GetID() string { return i.ClusterID }
func (i ClusterInfo) GetPurpose() string { return i.Purpose }
func (i ClusterInfo) GetExpiry() time.Time { return i.Expiry }
func (i ClusterInfo) GetState() string { return "ready" }
func (i ClusterInfo) GetID() string { return i.ClusterID }
func (i ClusterInfo) GetType() deployment.ClusterType { return i.Type }
func (i ClusterInfo) GetPurpose() string { return i.Purpose }
func (i ClusterInfo) GetExpiry() time.Time { return i.Expiry }
func (i ClusterInfo) GetState() string { return "ready" }
func (i ClusterInfo) GetNodes() []deployment.ClusterNodeInfo {
var nodes []deployment.ClusterNodeInfo
for _, node := range i.Nodes {
Expand Down
103 changes: 103 additions & 0 deletions deployment/dockerdeploy/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"

"github.com/couchbaselabs/cbdinocluster/utils/clustercontrol"
Expand All @@ -28,6 +29,7 @@ type Controller struct {

type NodeInfo struct {
ContainerID string
Type string
NodeID string
ClusterID string
Name string
Expand All @@ -41,6 +43,7 @@ type NodeInfo struct {

func (c *Controller) parseContainerInfo(container types.Container) *NodeInfo {
clusterID := container.Labels["com.couchbase.dyncluster.cluster_id"]
nodeType := container.Labels["com.couchbase.dyncluster.type"]
nodeID := container.Labels["com.couchbase.dyncluster.node_id"]
nodeName := container.Labels["com.couchbase.dyncluster.node_name"]
creator := container.Labels["com.couchbase.dyncluster.creator"]
Expand All @@ -57,8 +60,14 @@ func (c *Controller) parseContainerInfo(container types.Container) *NodeInfo {
pickedNetwork = network
}

// if the node type is unspecified, we default to server-node
if nodeType == "" {
nodeType = "server-node"
}

return &NodeInfo{
ContainerID: container.ID,
Type: nodeType,
NodeID: nodeID,
ClusterID: clusterID,
Name: nodeName,
Expand Down Expand Up @@ -182,12 +191,100 @@ func (c *Controller) ReadNodeState(ctx context.Context, containerID string) (*Do
}, nil
}

func (c *Controller) DeployS3MockNode(ctx context.Context, clusterID string, expiry time.Duration) (*NodeInfo, error) {
nodeID := "s3mock"
logger := c.Logger.With(zap.String("nodeId", nodeID))

logger.Debug("deploying s3mock node")

containerName := "cbdynnode-s3-" + clusterID

createResult, err := c.DockerCli.ContainerCreate(context.Background(), &container.Config{
Image: "adobe/s3mock",
Labels: map[string]string{
"com.couchbase.dyncluster.cluster_id": clusterID,
"com.couchbase.dyncluster.type": "s3mock",
"com.couchbase.dyncluster.purpose": "s3mock backing for columnar",
"com.couchbase.dyncluster.node_id": nodeID,
},
// same effect as ntp
Volumes: map[string]struct{}{"/etc/localtime:/etc/localtime": {}},
}, &container.HostConfig{
AutoRemove: true,
NetworkMode: container.NetworkMode(c.NetworkName),
CapAdd: []string{"NET_ADMIN"},
Resources: container.Resources{
Ulimits: []*units.Ulimit{
{Name: "nofile", Soft: 200000, Hard: 200000},
},
},
}, nil, nil, containerName)
if err != nil {
return nil, errors.Wrap(err, "failed to create container")
}

containerID := createResult.ID

logger.Debug("container created, starting", zap.String("container", containerID))

err = c.DockerCli.ContainerStart(context.Background(), containerID, types.ContainerStartOptions{})
if err != nil {
return nil, errors.Wrap(err, "failed to start container")
}

expiryTime := time.Time{}
if expiry > 0 {
expiryTime = time.Now().Add(expiry)
}

err = c.WriteNodeState(ctx, containerID, &DockerNodeState{
Expiry: expiryTime,
})
if err != nil {
return nil, errors.Wrap(err, "failed write node state")
}

// Cheap hack for simpler parsing...
allNodes, err := c.ListNodes(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed to list nodes")
}

var node *NodeInfo
for _, allNode := range allNodes {
if allNode.ContainerID == containerID {
node = allNode
}
}
if node == nil {
return nil, errors.New("failed to find newly created container")
}

logger.Debug("container has started, waiting for it to get ready", zap.String("address", node.IPAddress))

for {
resp, err := http.Get(fmt.Sprintf("http://%s:%d", node.IPAddress, 9090))
if err != nil || resp.StatusCode != 200 {
logger.Debug("s3mock not ready yet", zap.Error(err))
time.Sleep(100 * time.Millisecond)
continue
}

break
}

logger.Debug("container is ready!")

return node, nil
}

type DeployNodeOptions struct {
Purpose string
Expiry time.Duration
ClusterID string
Image *ImageRef
ImageServerVersion string
IsColumnar bool
EnvVars map[string]string
}

Expand All @@ -204,10 +301,16 @@ func (c *Controller) DeployNode(ctx context.Context, def *DeployNodeOptions) (*N
envVars = append(envVars, fmt.Sprintf("%s=%s", varName, varValue))
}

nodeType := "server-node"
if def.IsColumnar {
nodeType = "columnar-node"
}

createResult, err := c.DockerCli.ContainerCreate(context.Background(), &container.Config{
Image: def.Image.ImagePath,
Labels: map[string]string{
"com.couchbase.dyncluster.cluster_id": def.ClusterID,
"com.couchbase.dyncluster.type": nodeType,
"com.couchbase.dyncluster.purpose": def.Purpose,
"com.couchbase.dyncluster.node_id": nodeID,
"com.couchbase.dyncluster.initial_server_version": def.ImageServerVersion,
Expand Down
Loading
Loading