blob: 8db4c3d32a57ae626f2bee6894957bef82fef466 [file] [edit]
package actions
import (
"context"
_ "embed"
"errors"
"fmt"
"os"
"os/exec"
"time"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
//go:embed scripts/disable_verified_boot.sh
var disableVerifiedBootScript string
//go:embed scripts/unlock_root_fs.sh
var unlockRootFSScript string
func unlockGCPInstance(ctx context.Context, args []string) error {
glog.Info("checking gcp instance verified boot status")
disableArgs := append(args, "--command", fmt.Sprintf("sudo bash -c '%s'", disableVerifiedBootScript))
_, err := runGcloudComputeSSH(ctx, disableArgs, nil)
if err != nil {
exitErr, ok := errors.AsType[*exec.ExitError](err)
if !ok || exitErr.ExitCode() != 10 {
return fmt.Errorf("failed to disable verified boot on remote instance: %w", err)
}
glog.Info("rebooting remote instance to apply grub changes")
rebootArgs := append(args, "--command", "sudo bash -c 'sleep 1 && echo b > /proc/sysrq-trigger' > /dev/null 2>&1 &")
_, err = runGcloudComputeSSH(ctx, rebootArgs, &SSHOptions{
MaxRetries: 3,
RetryDelay: 2 * time.Second,
Timeout: 10 * time.Second,
})
if err != nil {
return fmt.Errorf("failed to trigger reboot: %w", err)
}
glog.Info("waiting for instance to become available")
waitArgs := append(args, "--command", "echo ready", "--ssh-flag=-o ConnectTimeout=5")
if _, err = runGcloudComputeSSH(ctx, waitArgs, &SSHOptions{
MaxRetries: 5,
RetryDelay: 10 * time.Second,
Timeout: 30 * time.Second,
}); err != nil {
return fmt.Errorf("instance failed to become available: %w", err)
}
} else {
glog.Info("verified boot is already disabled.")
}
glog.Info("unlocking gcp instance root fs")
unlockArgs := append(args, "--command", fmt.Sprintf("sudo bash -c '%s'", unlockRootFSScript))
_, err = runGcloudComputeSSH(ctx, unlockArgs, nil)
if err != nil {
exitErr, ok := errors.AsType[*exec.ExitError](err)
if !ok || exitErr.ExitCode() != 10 {
return fmt.Errorf("failed to unlock root fs on remote instance: %w", err)
}
glog.Info("root fs unlocked")
return nil
}
glog.Info("root fs already unlocked")
return nil
}
func unlockLocalInstance() error {
glog.Info("checking verified boot status")
_, err := executeScript(disableVerifiedBootScript)
if err != nil {
exitErr, ok := errors.AsType[*exec.ExitError](err)
if !ok || exitErr.ExitCode() != 10 {
return fmt.Errorf("failed to disable verified boot: %w", err)
}
glog.Info("rebooting to apply grub changes")
if err := os.WriteFile("/proc/sysrq-trigger", []byte("b"), 0644); err != nil {
return fmt.Errorf("failed to trigger reboot: %w", err)
}
return nil
}
glog.Info("verified boot is already disabled")
glog.Info("unlocking local instance root fs")
_, err = executeScript(unlockRootFSScript)
if err != nil {
exitErr, ok := errors.AsType[*exec.ExitError](err)
if !ok || exitErr.ExitCode() != 10 {
return fmt.Errorf("failed to unlock root fs: %w", err)
}
glog.Info("root fs unlocked")
return nil
}
glog.Info("root fs already unlocked")
return nil
}
var unlockCmd = &cobra.Command{
Use: "unlock",
Short: "Unlocks a local or remote GCP instance to allow development",
Long: `Executes a bash script to set up either a local instance or remote GCP instance for
development. If no arguments are given, then the script is run to unlock the local instance. If
arguments are given, then the arguments are passed into gcloud compute ssh to be executed on`,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return unlockLocalInstance()
} else {
return unlockGCPInstance(cmd.Context(), args)
}
},
}
func init() {
rootCmd.AddCommand(unlockCmd)
}