-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
adding milpactl for debugging, got rid of certs, etc
- Loading branch information
Showing
18 changed files
with
1,136 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package cmd | ||
|
||
import ( | ||
"bufio" | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"os" | ||
"strconv" | ||
|
||
"github.com/elotl/cloud-instance-provider/pkg/api" | ||
"github.com/elotl/cloud-instance-provider/pkg/clientapi" | ||
"github.com/elotl/wsstream" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
var ( | ||
attachPodName string | ||
attachUnitName string | ||
attachInteractive bool | ||
attachUsageStr = "attach POD_NAME" | ||
) | ||
|
||
func attach(cmd *cobra.Command, args []string) { | ||
if len(args) == 0 { | ||
fatal("A pod name is required: " + attachUsageStr) | ||
} | ||
attachPodName = args[0] | ||
|
||
params := api.AttachParams{ | ||
PodName: attachPodName, | ||
UnitName: attachUnitName, | ||
Interactive: attachInteractive, | ||
TTY: false, | ||
} | ||
|
||
client, conn, err := getMilpaClient(cmd.InheritedFlags(), false) | ||
dieIfError(err, "Failed to create milpa client") | ||
defer conn.Close() | ||
|
||
stream, err := client.Attach(context.Background()) | ||
dieIfError(err, "Failed to setup attach streaming client") | ||
|
||
b, err := json.Marshal(params) | ||
dieIfError(err, "Error serializing attach parameters") | ||
paramMsg := &clientapi.StreamMsg{Data: b} | ||
err = stream.Send(paramMsg) | ||
dieIfError(err, "Error sending initial attach parameters") | ||
|
||
// this looks a lot like exec. The count is at two. | ||
// https://en.wikipedia.org/wiki/Rule_of_three_(computer_programming) | ||
|
||
// Read from local stdin | ||
go func() { | ||
defer stream.CloseSend() | ||
// We read based on newlines. Using scanner won't work for | ||
// interactive programs but lets not worry about that now. | ||
scanner := bufio.NewScanner(os.Stdin) | ||
for scanner.Scan() { | ||
t := scanner.Text() + "\n" | ||
f := wsstream.PackMessage(wsstream.StdinChan, []byte(t)) | ||
sm := &clientapi.StreamMsg{Data: f} | ||
if err := stream.Send(sm); err != nil { | ||
return | ||
} | ||
} | ||
dieIfError(scanner.Err(), "Error reading stdin") | ||
}() | ||
|
||
// Write to local stdout and stderr, if we get an exit code, | ||
// exit with that code | ||
for { | ||
resp, err := stream.Recv() | ||
if err == io.EOF { | ||
return | ||
} | ||
dieIfError(err, "Error in grpc receive") | ||
c, msg, err := wsstream.UnpackMessage(resp.Data) | ||
dieIfError(err, "error unserializing websocket data") | ||
if len(msg) > 0 { | ||
if c == wsstream.StdoutChan { | ||
fmt.Fprint(os.Stdout, string(msg)) | ||
} else if c == wsstream.StderrChan { | ||
fmt.Fprint(os.Stderr, string(msg)) | ||
} else if c == wsstream.ExitCodeChan { | ||
i, err := strconv.Atoi(string(msg)) | ||
if err != nil { | ||
errmsg := fmt.Sprintf("Invalid exit code: %s", msg) | ||
fmt.Fprint(os.Stderr, errmsg) | ||
os.Exit(1) | ||
} | ||
os.Exit(i) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func AttachCommand() *cobra.Command { | ||
var attachCmd = &cobra.Command{ | ||
Use: "attach", | ||
Short: "Attach to a process that is already running inside an existing unit", | ||
Long: `Attach to a process that is already running inside an existing unit`, | ||
Example: `# Get output from running pod my-pod, using the first unit by default | ||
milpactl attach my-pod | ||
# Get output from rubyserver unit from pod my-pod | ||
milpactl attach my-pod -u rubyserver | ||
# Send stdin to rubyserver in pod my-pod and sends stdout/stderr from rubyserver back to the client | ||
milpactl attach my-pod -u rubyserver -i`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
attach(cmd, args) | ||
}, | ||
} | ||
|
||
attachCmd.Flags().StringVarP(&attachUnitName, "unit", "u", "", "Unit name. If empty the first unit in the pod will be used") | ||
attachCmd.Flags().BoolVarP(&attachInteractive, "stdin", "i", false, "Pass stdin to the unit") | ||
|
||
return attachCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"os" | ||
"path/filepath" | ||
"time" | ||
|
||
"github.com/elotl/cloud-instance-provider/pkg/clientapi" | ||
"github.com/elotl/cloud-instance-provider/pkg/util" | ||
"github.com/spf13/pflag" | ||
"golang.org/x/net/context" | ||
"google.golang.org/grpc" | ||
) | ||
|
||
var ( | ||
grpcDialTimeout = 5 * time.Second | ||
) | ||
|
||
// Note: this can get called concurrently and cobra.Cmd.InheritedFlags | ||
// is not safe for concurrent access. | ||
func getMilpaClient(flags *pflag.FlagSet, needsLeader bool) (clientapi.MilpaClient, *grpc.ClientConn, error) { | ||
endpoints, err := flags.GetStringSlice("endpoints") | ||
if err != nil { | ||
return nil, nil, util.WrapError(err, "Error getting endpoints argument") | ||
} | ||
// We shuffle endpoints to do some weak loadbalancing | ||
rand.Seed(time.Now().UTC().UnixNano()) | ||
order := rand.Perm(len(endpoints)) | ||
for i, _ := range order { | ||
address := endpoints[i] | ||
var ( | ||
client clientapi.MilpaClient | ||
conn *grpc.ClientConn | ||
) | ||
client, conn, err = connectToServer(context.Background(), address, flags) | ||
if err != nil { | ||
// If we got an error with that server, just continue | ||
// trying other servers | ||
continue | ||
} | ||
|
||
if !needsLeader { | ||
return client, conn, nil | ||
} else { | ||
var foundLeader bool | ||
foundLeader, err = isLeader(context.Background(), client) | ||
if foundLeader { | ||
return client, conn, nil | ||
} else if err == nil { | ||
err = fmt.Errorf("leader required") | ||
} | ||
} | ||
_ = conn.Close() | ||
} | ||
msg := "Could not connect to the Milpa API server: " + err.Error() | ||
return nil, nil, fmt.Errorf(msg) | ||
} | ||
|
||
func isLeader(ctx context.Context, client clientapi.MilpaClient) (bool, error) { | ||
req := clientapi.IsLeaderRequest{} | ||
reply, err := client.IsLeader(ctx, &req) | ||
if err != nil { | ||
return false, fmt.Errorf("Error querying milpa server: %v", err.Error()) | ||
} | ||
return reply.IsLeader, nil | ||
} | ||
|
||
func getWorkingDir() string { | ||
dir, err := filepath.Abs(filepath.Dir(os.Args[0])) | ||
dieIfError(err, "error checking working directory") | ||
return dir | ||
} | ||
|
||
func connectToServer(ctx context.Context, serverAddress string, flags *pflag.FlagSet) (clientapi.MilpaClient, *grpc.ClientConn, error) { | ||
timeoutCtx, cancel := context.WithTimeout(ctx, grpcDialTimeout) | ||
defer cancel() | ||
conn, err := grpc.DialContext( | ||
timeoutCtx, serverAddress, grpc.WithInsecure(), grpc.WithBlock()) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
return clientapi.NewMilpaClient(conn), conn, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
func create(cmd *cobra.Command, args []string) { | ||
appManifestFile, err := cmd.Flags().GetString("file") | ||
dieIfError(err, "Error accessing 'file' flag for cmd %s", cmd.Name()) | ||
client, conn, err := getMilpaClient(cmd.InheritedFlags(), true) | ||
dieIfError(err, "Failed to create milpa client") | ||
defer conn.Close() | ||
errors := modify(client, appManifestFile, modifyCreate) | ||
if len(errors) > 0 { | ||
fatal("Failed to create some resources: %v", errors) | ||
} | ||
} | ||
|
||
func CreateCommand() *cobra.Command { | ||
var createCmd = &cobra.Command{ | ||
Use: "create", | ||
Short: "Create milpa object", | ||
Long: `Create object specified in manifest on cloud of choice`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
create(cmd, args) | ||
}, | ||
} | ||
createCmd.Flags().StringP("file", "f", "", "Fully qualified path to manifest file") | ||
return createCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/elotl/cloud-instance-provider/pkg/clientapi" | ||
"github.com/elotl/cloud-instance-provider/pkg/milpactl" | ||
"github.com/elotl/cloud-instance-provider/pkg/util" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
func del(cmd *cobra.Command, args []string) { | ||
// see if app manifest file has been supplied | ||
if len(args) > 0 && len(args) != 2 { | ||
fatal("Usage: milpactl delete <resource> <name>") | ||
} | ||
cascade, _ := cmd.Flags().GetBool("cascade") | ||
|
||
client, conn, err := getMilpaClient(cmd.InheritedFlags(), true) | ||
dieIfError(err, "Failed to create milpa client") | ||
defer conn.Close() | ||
|
||
if len(args) == 2 { | ||
kind := milpactl.CleanupResourceName(args[0]) | ||
name := args[1] | ||
if !util.StringInSlice(kind, deleteTypes) { | ||
fatal("Illegal resource type: %s", kind) | ||
} | ||
deleteRequest := &clientapi.DeleteRequest{ | ||
Kind: []byte(kind), | ||
Name: []byte(name), | ||
Cascade: cascade, | ||
} | ||
reply, err := client.Delete(context.Background(), deleteRequest) | ||
dieIfError(err, "Could not delete resource") | ||
dieIfReplyError("Delete", reply) | ||
fmt.Printf("%s\n", name) | ||
} else { | ||
manifestFile, err := cmd.Flags().GetString("file") | ||
dieIfError(err, "Error accessing 'file' flag for cmd %s", cmd.Name()) | ||
op := modifyDeleteCascade | ||
if !cascade { | ||
op = modifyDelete | ||
} | ||
errors := modify(client, manifestFile, op) | ||
if len(errors) > 0 { | ||
fatal("Failed to update some resources: %v", errors) | ||
} | ||
} | ||
} | ||
|
||
func DeleteCommand() *cobra.Command { | ||
var deleteCmd = &cobra.Command{ | ||
Use: "delete ([-f filename] | (<resource> <name>))", | ||
Short: "Delete resource by filename or by resource and name", | ||
Long: `Delete resource by filename or by resource and name`, | ||
Example: ` | ||
# Delete a pod using the type and name specified in the file pod.yml. | ||
milpactl delete -f ./pod.yml | ||
# Delete a pod named mypod | ||
milpactl delete pod mypod | ||
# Delete a deployment named mydeployment and delete all objects managed by that deployment | ||
milpactl delete --cascade deployment mypod`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
del(cmd, args) | ||
}, | ||
} | ||
deleteCmd.Flags().BoolP("cascade", "", true, "If true, cascade the deletion of the resources managed by this resource") | ||
deleteCmd.Flags().StringP("file", "f", "", "Fully qualified path to manifest file") | ||
return deleteCmd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package cmd | ||
|
||
import ( | ||
"io" | ||
"os" | ||
|
||
"github.com/elotl/cloud-instance-provider/pkg/clientapi" | ||
"github.com/spf13/cobra" | ||
"golang.org/x/net/context" | ||
) | ||
|
||
func deploy(cmd *cobra.Command, args []string) { | ||
resourceName := args[0] | ||
itemName := args[1] | ||
pkgfile := args[2] | ||
|
||
client, conn, err := getMilpaClient(cmd.InheritedFlags(), false) | ||
dieIfError(err, "Failed to create milpa client") | ||
defer conn.Close() | ||
|
||
req := &clientapi.DeployRequest{ | ||
ResourceName: resourceName, | ||
ItemName: itemName, | ||
} | ||
f, err := os.Open(pkgfile) | ||
dieIfError(err, "Could not open package file %s", pkgfile) | ||
stream, err := client.Deploy(context.Background()) | ||
dieIfError(err, | ||
"Could not deploy %s for %s/%s", pkgfile, resourceName, itemName) | ||
for { | ||
buf := make([]byte, 64*1024) // Recommended chunk size for streaming. | ||
_, err := f.Read(buf) | ||
if err == io.EOF { | ||
break | ||
} | ||
dieIfError(err, "Could not read package file %s", pkgfile) | ||
req.PackageData = buf | ||
err = stream.Send(req) | ||
dieIfError(err, "Could not send package data") | ||
} | ||
reply, err := stream.CloseAndRecv() | ||
dieIfError(err, | ||
"Could not deploy %s for %s/%s", pkgfile, resourceName, itemName) | ||
dieIfReplyError("Deploy", reply) | ||
} | ||
|
||
func DeployCommand() *cobra.Command { | ||
var deployCmd = &cobra.Command{ | ||
Use: "deploy pod_name package_name package_file", | ||
Short: "Deploy Milpa package for a pod", | ||
Long: `Deploy Milpa package for a pod`, | ||
Args: cobra.RangeArgs(3, 3), | ||
Run: deploy, | ||
} | ||
return deployCmd | ||
} |
Oops, something went wrong.