diff --git a/cmd/auth/auth.go b/cmd/auth/auth.go index 2f64b6e..5be259e 100644 --- a/cmd/auth/auth.go +++ b/cmd/auth/auth.go @@ -1,18 +1,10 @@ package auth import ( - "encoding/json" - "errors" "fmt" "os" - "os/exec" - "sort" - "strings" - "github.com/brevdev/nvcf/api" "github.com/brevdev/nvcf/config" - "github.com/brevdev/nvcf/output" - "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" ) @@ -41,303 +33,3 @@ func AuthCmd() *cobra.Command { return cmd } - -func authLoginCmd() *cobra.Command { - return &cobra.Command{ - Use: "login", - Short: "Authenticate with NVIDIA Cloud", - RunE: func(cmd *cobra.Command, args []string) error { - apiKey := output.Prompt("Enter your NVIDIA Cloud API key: ", true) - - err := config.SetAPIKey(apiKey) - if err != nil { - return output.Error(cmd, "Error saving API key", err) - } - - // Use the API key to get the first org - client := api.NewClient(apiKey) - orgsInfo := map[string]interface{}{} - err = client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo) - if err != nil { - return output.Error(cmd, "Failed to fetch organization information", err) - } - - organizations, ok := orgsInfo["organizations"].([]interface{}) - if !ok || len(organizations) == 0 { - return output.Error(cmd, "No organizations found", nil) - } - - firstOrg, ok := organizations[0].(map[string]interface{}) - if !ok { - return output.Error(cmd, "Failed to parse organization information", nil) - } - - orgID, ok := firstOrg["name"].(string) - if !ok { - return output.Error(cmd, "Organization ID not found", nil) - } - - err = config.SetOrgID(orgID) - if err != nil { - return output.Error(cmd, "Error saving Org ID", err) - } - - output.PrintASCIIArt(cmd) - output.Success(cmd, fmt.Sprintf("Authentication successful. You are now authenticated with organization ID: %s", orgID)) - return nil - }, - } -} - -func authConfigureDockerCmd() *cobra.Command { - return &cobra.Command{ - Use: "configure-docker", - Short: "Configure Docker to use NGC API key for nvcr.io", - RunE: func(cmd *cobra.Command, args []string) error { - apiKey := config.GetAPIKey() - if apiKey == "" { - return output.Error(cmd, "NGC API key not found. Please run 'nvcf auth login' first.", nil) - } - // Check if Docker is installed - _, err := exec.LookPath("docker") - if err != nil { - return output.Error(cmd, "Docker is not installed or not in the system PATH", err) - } - // TODO: check for existing nvcr.io config? - dockerCmd := exec.Command("docker", "login", "nvcr.io", "-u", "$oauthtoken", "--password-stdin") - dockerCmd.Stdin = strings.NewReader(apiKey) - out, err := dockerCmd.CombinedOutput() - if err != nil { - cmd.Println(string(out)) - return output.Error(cmd, "Failed to configure Docker", err) - } - output.Success(cmd, "Docker configured successfully for nvcr.io") - cmd.Println(string(out)) - return nil - }, - } -} - -func authStatusCmd() *cobra.Command { - return &cobra.Command{ - Use: "status", - Short: "Check the authentication status", - RunE: func(cmd *cobra.Command, args []string) error { - if !config.IsAuthenticated() { - return output.Error(cmd, "Not authenticated", errors.New("no API key found")) - } - - client := api.NewClient(config.GetAPIKey()) - - userInfo := map[string]interface{}{} - err := client.Get(cmd.Context(), "/v2/users/me", nil, &userInfo) - if err != nil { - return output.Error(cmd, "Failed to fetch user information", err) - } - - orgsInfo := map[string]interface{}{} - err = client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo) - if err != nil { - return output.Error(cmd, "Failed to fetch organization information", err) - } - - user, _ := userInfo["user"].(map[string]interface{}) - email, _ := user["email"].(string) - name, _ := user["name"].(string) - currentOrgID := config.GetOrgID() - - output.Success(cmd, "Authenticated") - fmt.Printf("User: %s (%s)\n", name, email) - fmt.Printf("Current Organization ID: %s\n", currentOrgID) - return nil - }, - } -} - -func authLogoutCmd() *cobra.Command { - return &cobra.Command{ - Use: "logout", - Short: "Logout from NVIDIA Cloud", - RunE: func(cmd *cobra.Command, args []string) error { - if !config.IsAuthenticated() { - output.Info(cmd, "You are currently not logged in") - return nil - } - err := config.ClearAPIKey() - if err != nil { - return output.Error(cmd, "Failed to clear API key", err) - } - err = config.ClearOrgID() - if err != nil { - - return output.Error(cmd, "Failed to clear Org ID", err) - } - output.Success(cmd, "Logged out successfully") - return nil - }, - } -} - -var whoamiURL = "/v2/users/me" - -func authWhoAmICmd() *cobra.Command { - return &cobra.Command{ - Use: "whoami", - Short: "Display information about the authenticated user", - RunE: func(cmd *cobra.Command, args []string) error { - client := api.NewClient(config.GetAPIKey()) - whoamiInfo := map[string]any{} - err := client.Get(cmd.Context(), whoamiURL, nil, &whoamiInfo) - if err != nil { - return output.Error(cmd, "Failed to fetch user information", err) - } - - jsonMode, _ := cmd.Flags().GetBool("json") - if jsonMode { - err = json.NewEncoder(cmd.OutOrStdout()).Encode(whoamiInfo) - if err != nil { - return output.Error(cmd, "Failed to encode user information", err) - } - return nil - } - userInfo, _ := whoamiInfo["user"].(map[string]any) - table := tablewriter.NewWriter(cmd.OutOrStdout()) - table.SetHeader([]string{"Email", "Name"}) - table.SetBorder(false) - table.Append([]string{ - userInfo["email"].(string), - userInfo["name"].(string), - }) - table.Render() - return nil - }, - } -} - -func authOrgsCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "orgs", - Short: "Display organization and team information for the authenticated user", - RunE: func(cmd *cobra.Command, args []string) error { - client := api.NewClient(config.GetAPIKey()) - userInfo := map[string]interface{}{} - err := client.Get(cmd.Context(), "/v2/users/me", nil, &userInfo) - if err != nil { - return output.Error(cmd, "Failed to fetch user information", err) - } - jsonMode, _ := cmd.Flags().GetBool("json") - if jsonMode { - err = json.NewEncoder(cmd.OutOrStdout()).Encode(userInfo) - if err != nil { - return output.Error(cmd, "Failed to encode user information", err) - } - return nil - } - userRoles, ok := userInfo["userRoles"].([]interface{}) - if !ok { - return output.Error(cmd, "Failed to parse user roles information", nil) - } - type OrgTeamInfo struct { - OrgName string - OrgDisplayName string - OrgType string - TeamName string - OrgRoles string - } - var orgTeamList []OrgTeamInfo - for _, role := range userRoles { - roleMap, ok := role.(map[string]interface{}) - if !ok { - continue - } - org, ok := roleMap["org"].(map[string]interface{}) - if !ok { - continue - } - team, _ := roleMap["team"].(map[string]interface{}) - orgName, _ := org["name"].(string) - orgDisplayName, _ := org["displayName"].(string) - orgType, _ := org["type"].(string) - teamName, _ := team["name"].(string) - orgRoles, _ := roleMap["orgRoles"].([]interface{}) - orgRolesStr := strings.Join(convertToStringSlice(orgRoles), ",") - orgTeamList = append(orgTeamList, OrgTeamInfo{ - OrgName: orgName, - OrgDisplayName: orgDisplayName, - OrgType: orgType, - TeamName: teamName, - OrgRoles: orgRolesStr, - }) - } - // Sort the list by org name, then team name - sort.Slice(orgTeamList, func(i, j int) bool { - if orgTeamList[i].OrgName == orgTeamList[j].OrgName { - return orgTeamList[i].TeamName < orgTeamList[j].TeamName - } - return orgTeamList[i].OrgName < orgTeamList[j].OrgName - }) - - wideMode, _ := cmd.Flags().GetBool("wide") - table := tablewriter.NewWriter(cmd.OutOrStdout()) - if wideMode { - table.SetHeader([]string{"Org Name", "Team Name", "Org Roles"}) - } else { - table.SetHeader([]string{"Org Name", "Org Display Name", "Org Type", "Team Name"}) - } - table.SetBorder(false) - for _, info := range orgTeamList { - if wideMode { - table.Append([]string{info.OrgName, info.TeamName, info.OrgRoles}) - } else { - table.Append([]string{info.OrgName, info.OrgDisplayName, info.OrgType, info.TeamName}) - } - } - table.Render() - return nil - }, - } - - cmd.Flags().BoolP("wide", "o", false, "Display wide output including org roles") - return cmd -} - -func authOrgIDCmd() *cobra.Command { - return &cobra.Command{ - Use: "org-id", - Short: "Display the name of the first organization", - RunE: func(cmd *cobra.Command, args []string) error { - client := api.NewClient(config.GetAPIKey()) - orgsInfo := map[string]interface{}{} - err := client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo) - if err != nil { - return output.Error(cmd, "Failed to fetch organization information", err) - } - - organizations, ok := orgsInfo["organizations"].([]interface{}) - if !ok || len(organizations) == 0 { - return output.Error(cmd, "No organizations found", nil) - } - - firstOrg, ok := organizations[0].(map[string]interface{}) - if !ok { - return output.Error(cmd, "Failed to parse organization information", nil) - } - - name, ok := firstOrg["name"].(string) - if !ok { - return output.Error(cmd, "Organization name not found", nil) - } - - fmt.Println(name) - return nil - }, - } -} - -func convertToStringSlice(slice []interface{}) []string { - result := make([]string, len(slice)) - for i, v := range slice { - result[i] = fmt.Sprint(v) - } - return result -} diff --git a/cmd/auth/auth_configure_docker.go b/cmd/auth/auth_configure_docker.go new file mode 100644 index 0000000..4795072 --- /dev/null +++ b/cmd/auth/auth_configure_docker.go @@ -0,0 +1,39 @@ +package auth + +import ( + "os/exec" + "strings" + + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/spf13/cobra" +) + +func authConfigureDockerCmd() *cobra.Command { + return &cobra.Command{ + Use: "configure-docker", + Short: "Configure Docker to use NGC API key for nvcr.io", + RunE: func(cmd *cobra.Command, args []string) error { + apiKey := config.GetAPIKey() + if apiKey == "" { + return output.Error(cmd, "NGC API key not found. Please run 'nvcf auth login' first.", nil) + } + // Check if Docker is installed + _, err := exec.LookPath("docker") + if err != nil { + return output.Error(cmd, "Docker is not installed or not in the system PATH", err) + } + // TODO: check for existing nvcr.io config? + dockerCmd := exec.Command("docker", "login", "nvcr.io", "-u", "$oauthtoken", "--password-stdin") + dockerCmd.Stdin = strings.NewReader(apiKey) + out, err := dockerCmd.CombinedOutput() + if err != nil { + cmd.Println(string(out)) + return output.Error(cmd, "Failed to configure Docker", err) + } + output.Success(cmd, "Docker configured successfully for nvcr.io") + cmd.Println(string(out)) + return nil + }, + } +} diff --git a/cmd/auth/auth_login.go b/cmd/auth/auth_login.go new file mode 100644 index 0000000..6cd5974 --- /dev/null +++ b/cmd/auth/auth_login.go @@ -0,0 +1,57 @@ +package auth + +import ( + "fmt" + + "github.com/brevdev/nvcf/api" + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/spf13/cobra" +) + +func authLoginCmd() *cobra.Command { + return &cobra.Command{ + Use: "login", + Short: "Authenticate with NVIDIA Cloud", + RunE: func(cmd *cobra.Command, args []string) error { + apiKey := output.Prompt("Enter your NVIDIA Cloud API key: ", true) + + err := config.SetAPIKey(apiKey) + if err != nil { + return output.Error(cmd, "Error saving API key", err) + } + + // Use the API key to get the first org + client := api.NewClient(apiKey) + orgsInfo := map[string]interface{}{} + err = client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo) + if err != nil { + return output.Error(cmd, "Failed to fetch organization information", err) + } + + organizations, ok := orgsInfo["organizations"].([]interface{}) + if !ok || len(organizations) == 0 { + return output.Error(cmd, "No organizations found", nil) + } + + firstOrg, ok := organizations[0].(map[string]interface{}) + if !ok { + return output.Error(cmd, "Failed to parse organization information", nil) + } + + orgID, ok := firstOrg["name"].(string) + if !ok { + return output.Error(cmd, "Organization ID not found", nil) + } + + err = config.SetOrgID(orgID) + if err != nil { + return output.Error(cmd, "Error saving Org ID", err) + } + + output.PrintASCIIArt(cmd) + output.Success(cmd, fmt.Sprintf("Authentication successful. You are now authenticated with organization ID: %s", orgID)) + return nil + }, + } +} diff --git a/cmd/auth/auth_logout.go b/cmd/auth/auth_logout.go new file mode 100644 index 0000000..df36095 --- /dev/null +++ b/cmd/auth/auth_logout.go @@ -0,0 +1,31 @@ +package auth + +import ( + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/spf13/cobra" +) + +func authLogoutCmd() *cobra.Command { + return &cobra.Command{ + Use: "logout", + Short: "Logout from NVIDIA Cloud", + RunE: func(cmd *cobra.Command, args []string) error { + if !config.IsAuthenticated() { + output.Info(cmd, "You are currently not logged in") + return nil + } + err := config.ClearAPIKey() + if err != nil { + return output.Error(cmd, "Failed to clear API key", err) + } + err = config.ClearOrgID() + if err != nil { + + return output.Error(cmd, "Failed to clear Org ID", err) + } + output.Success(cmd, "Logged out successfully") + return nil + }, + } +} diff --git a/cmd/auth/auth_orgid.go b/cmd/auth/auth_orgid.go new file mode 100644 index 0000000..f7a9d24 --- /dev/null +++ b/cmd/auth/auth_orgid.go @@ -0,0 +1,52 @@ +package auth + +import ( + "context" + "fmt" + + "github.com/brevdev/nvcf/api" + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/spf13/cobra" +) + +func authOrgIDCmd() *cobra.Command { + return &cobra.Command{ + Use: "org-id", + Short: "Display the name of the first organization", + RunE: func(cmd *cobra.Command, args []string) error { + orgId, err := GetOrgId() + if err != nil { + return output.Error(cmd, "Failed to fetch organization information", err) + } + fmt.Println(orgId) + return nil + }, + } +} + +func GetOrgId() (string, error) { + client := api.NewClient(config.GetAPIKey()) + orgsInfo := map[string]interface{}{} + err := client.Get(context.Background(), "/v2/orgs", nil, &orgsInfo) + if err != nil { + return "", err + } + + organizations, ok := orgsInfo["organizations"].([]interface{}) + if !ok || len(organizations) == 0 { + return "", fmt.Errorf("no organizations found") + } + + firstOrg, ok := organizations[0].(map[string]interface{}) + if !ok { + return "", fmt.Errorf("failed to parse organization information") + } + + name, ok := firstOrg["name"].(string) + if !ok { + return "", fmt.Errorf("organization name not found") + } + + return name, nil +} diff --git a/cmd/auth/auth_orgs.go b/cmd/auth/auth_orgs.go new file mode 100644 index 0000000..32a98bb --- /dev/null +++ b/cmd/auth/auth_orgs.go @@ -0,0 +1,109 @@ +package auth + +import ( + "encoding/json" + "fmt" + "sort" + "strings" + + "github.com/brevdev/nvcf/api" + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +func authOrgsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "orgs", + Short: "Display organization and team information for the authenticated user", + RunE: func(cmd *cobra.Command, args []string) error { + client := api.NewClient(config.GetAPIKey()) + userInfo := map[string]interface{}{} + err := client.Get(cmd.Context(), "/v2/users/me", nil, &userInfo) + if err != nil { + return output.Error(cmd, "Failed to fetch user information", err) + } + jsonMode, _ := cmd.Flags().GetBool("json") + if jsonMode { + err = json.NewEncoder(cmd.OutOrStdout()).Encode(userInfo) + if err != nil { + return output.Error(cmd, "Failed to encode user information", err) + } + return nil + } + userRoles, ok := userInfo["userRoles"].([]interface{}) + if !ok { + return output.Error(cmd, "Failed to parse user roles information", nil) + } + type OrgTeamInfo struct { + OrgName string + OrgDisplayName string + OrgType string + TeamName string + OrgRoles string + } + var orgTeamList []OrgTeamInfo + for _, role := range userRoles { + roleMap, ok := role.(map[string]interface{}) + if !ok { + continue + } + org, ok := roleMap["org"].(map[string]interface{}) + if !ok { + continue + } + team, _ := roleMap["team"].(map[string]interface{}) + orgName, _ := org["name"].(string) + orgDisplayName, _ := org["displayName"].(string) + orgType, _ := org["type"].(string) + teamName, _ := team["name"].(string) + orgRoles, _ := roleMap["orgRoles"].([]interface{}) + orgRolesStr := strings.Join(convertToStringSlice(orgRoles), ",") + orgTeamList = append(orgTeamList, OrgTeamInfo{ + OrgName: orgName, + OrgDisplayName: orgDisplayName, + OrgType: orgType, + TeamName: teamName, + OrgRoles: orgRolesStr, + }) + } + // Sort the list by org name, then team name + sort.Slice(orgTeamList, func(i, j int) bool { + if orgTeamList[i].OrgName == orgTeamList[j].OrgName { + return orgTeamList[i].TeamName < orgTeamList[j].TeamName + } + return orgTeamList[i].OrgName < orgTeamList[j].OrgName + }) + + wideMode, _ := cmd.Flags().GetBool("wide") + table := tablewriter.NewWriter(cmd.OutOrStdout()) + if wideMode { + table.SetHeader([]string{"Org Name", "Team Name", "Org Roles"}) + } else { + table.SetHeader([]string{"Org Name", "Org Display Name", "Org Type", "Team Name"}) + } + table.SetBorder(false) + for _, info := range orgTeamList { + if wideMode { + table.Append([]string{info.OrgName, info.TeamName, info.OrgRoles}) + } else { + table.Append([]string{info.OrgName, info.OrgDisplayName, info.OrgType, info.TeamName}) + } + } + table.Render() + return nil + }, + } + + cmd.Flags().BoolP("wide", "o", false, "Display wide output including org roles") + return cmd +} + +func convertToStringSlice(slice []interface{}) []string { + result := make([]string, len(slice)) + for i, v := range slice { + result[i] = fmt.Sprint(v) + } + return result +} diff --git a/cmd/auth/auth_status.go b/cmd/auth/auth_status.go new file mode 100644 index 0000000..56a4951 --- /dev/null +++ b/cmd/auth/auth_status.go @@ -0,0 +1,47 @@ +package auth + +import ( + "errors" + "fmt" + + "github.com/brevdev/nvcf/api" + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/spf13/cobra" +) + +func authStatusCmd() *cobra.Command { + return &cobra.Command{ + Use: "status", + Short: "Check the authentication status", + RunE: func(cmd *cobra.Command, args []string) error { + if !config.IsAuthenticated() { + return output.Error(cmd, "Not authenticated", errors.New("no API key found")) + } + + client := api.NewClient(config.GetAPIKey()) + + userInfo := map[string]interface{}{} + err := client.Get(cmd.Context(), "/v2/users/me", nil, &userInfo) + if err != nil { + return output.Error(cmd, "Failed to fetch user information", err) + } + + orgsInfo := map[string]interface{}{} + err = client.Get(cmd.Context(), "/v2/orgs", nil, &orgsInfo) + if err != nil { + return output.Error(cmd, "Failed to fetch organization information", err) + } + + user, _ := userInfo["user"].(map[string]interface{}) + email, _ := user["email"].(string) + name, _ := user["name"].(string) + currentOrgID := config.GetOrgID() + + output.Success(cmd, "Authenticated") + fmt.Printf("User: %s (%s)\n", name, email) + fmt.Printf("Current Organization ID: %s\n", currentOrgID) + return nil + }, + } +} diff --git a/cmd/auth/auth_whoami.go b/cmd/auth/auth_whoami.go new file mode 100644 index 0000000..a384620 --- /dev/null +++ b/cmd/auth/auth_whoami.go @@ -0,0 +1,47 @@ +package auth + +import ( + "encoding/json" + + "github.com/brevdev/nvcf/api" + "github.com/brevdev/nvcf/config" + "github.com/brevdev/nvcf/output" + "github.com/olekukonko/tablewriter" + "github.com/spf13/cobra" +) + +var whoamiURL = "/v2/users/me" + +func authWhoAmICmd() *cobra.Command { + return &cobra.Command{ + Use: "whoami", + Short: "Display information about the authenticated user", + RunE: func(cmd *cobra.Command, args []string) error { + client := api.NewClient(config.GetAPIKey()) + whoamiInfo := map[string]any{} + err := client.Get(cmd.Context(), whoamiURL, nil, &whoamiInfo) + if err != nil { + return output.Error(cmd, "Failed to fetch user information", err) + } + + jsonMode, _ := cmd.Flags().GetBool("json") + if jsonMode { + err = json.NewEncoder(cmd.OutOrStdout()).Encode(whoamiInfo) + if err != nil { + return output.Error(cmd, "Failed to encode user information", err) + } + return nil + } + userInfo, _ := whoamiInfo["user"].(map[string]any) + table := tablewriter.NewWriter(cmd.OutOrStdout()) + table.SetHeader([]string{"Email", "Name"}) + table.SetBorder(false) + table.Append([]string{ + userInfo["email"].(string), + userInfo["name"].(string), + }) + table.Render() + return nil + }, + } +} diff --git a/cmd/preflight/container/container.go b/cmd/preflight/container/container.go new file mode 100644 index 0000000..3e8d79b --- /dev/null +++ b/cmd/preflight/container/container.go @@ -0,0 +1,18 @@ +package container + +import ( + "github.com/spf13/cobra" +) + +func ContainerCmd() *cobra.Command { + cmd := &cobra.Command{ + Aliases: []string{"c", "container"}, + Use: "container", + Short: "Push a container to nvcr.io to start using it with NVCF", + Long: `Push a container to nvcr.io to start using it with NVCF`, + } + + cmd.AddCommand(PushCmd()) + + return cmd +} diff --git a/cmd/preflight/container/container_push.go b/cmd/preflight/container/container_push.go new file mode 100644 index 0000000..1edc024 --- /dev/null +++ b/cmd/preflight/container/container_push.go @@ -0,0 +1,97 @@ +package container + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "github.com/brevdev/nvcf/cmd/auth" + "github.com/spf13/cobra" +) + +func PushCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "push", + Short: "Tag a new container and push it to nvcr.io", + Long: "Tag a new container and push it to nvcr.io", + Args: cobra.ExactArgs(1), + RunE: runPush, + } + + cmd.Flags().StringP("tag", "t", "latest", "Tag to push") + + return cmd +} + +func runPush(cmd *cobra.Command, args []string) error { + image := args[0] + tag, err := cmd.Flags().GetString("tag") + if err != nil { + return err + } + + // Check if the image exists locally + if err := checkAndPullImage(image, tag); err != nil { + return err + } + + // Get the orgId + orgId, err := auth.GetOrgId() + if err != nil { + return fmt.Errorf("failed to get organization ID: %w", err) + } + + // Split the image name to separate the repository and image name + imageParts := strings.Split(image, "/") + imageName := imageParts[len(imageParts)-1] + + // Construct the new image name + newImageName := fmt.Sprintf("nvcr.io/%s/%s", orgId, imageName) + + // Retag the image + retagCmd := exec.Command("docker", "tag", fmt.Sprintf("%s:%s", image, tag), fmt.Sprintf("%s:%s", newImageName, tag)) + if output, err := retagCmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to retag image: %s, %w", string(output), err) + } + + fmt.Printf("Successfully retagged image as %s:%s\n", newImageName, tag) + + // Push the new image + pushCmd := exec.Command("docker", "push", fmt.Sprintf("%s:%s", newImageName, tag)) + + // Set up pipes to capture and display output in real-time + pushCmd.Stdout = os.Stdout + pushCmd.Stderr = os.Stderr + + if err := pushCmd.Run(); err != nil { + return fmt.Errorf("failed to push image: %w", err) + } + + fmt.Printf("Successfully pushed image %s:%s to nvcr.io\n", newImageName, tag) + + return nil +} + +func checkAndPullImage(image, tag string) error { + // Check if the image exists locally + inspectCmd := exec.Command("docker", "inspect", fmt.Sprintf("%s:%s", image, tag)) + if err := inspectCmd.Run(); err == nil { + // Image exists locally + fmt.Printf("Image %s:%s found locally\n", image, tag) + return nil + } + + // Image doesn't exist locally, pull it + fmt.Printf("Image %s:%s not found locally. Pulling...\n", image, tag) + pullCmd := exec.Command("docker", "pull", fmt.Sprintf("%s:%s", image, tag)) + pullCmd.Stdout = os.Stdout + pullCmd.Stderr = os.Stderr + + if err := pullCmd.Run(); err != nil { + return fmt.Errorf("failed to pull image %s:%s: %w", image, tag, err) + } + + fmt.Printf("Successfully pulled image %s:%s\n", image, tag) + return nil +} diff --git a/cmd/preflight/preflight.go b/cmd/preflight/preflight.go index 837deaf..bd6a467 100644 --- a/cmd/preflight/preflight.go +++ b/cmd/preflight/preflight.go @@ -2,6 +2,7 @@ package preflight import ( "github.com/brevdev/nvcf/cmd/preflight/check" + "github.com/brevdev/nvcf/cmd/preflight/container" "github.com/brevdev/nvcf/cmd/preflight/debug" "github.com/brevdev/nvcf/config" "github.com/spf13/cobra" @@ -20,5 +21,6 @@ func PreflightCmd() *cobra.Command { cmd.AddCommand(check.CheckCmd()) cmd.AddCommand(debug.DebugCmd()) + cmd.AddCommand(container.ContainerCmd()) return cmd } diff --git a/deploy b/deploy new file mode 100644 index 0000000..e69de29 diff --git a/deploy2.yaml b/deploy2.yaml new file mode 100644 index 0000000..30abf21 --- /dev/null +++ b/deploy2.yaml @@ -0,0 +1,23 @@ +fn_image: nvcr.io/0514617533638406/development/nvcf-jupyter-lab:0.0.8 +functions: + - name: test-jupyter-lab + inferenceUrl: "/echo" + inferencePort: 8000 + healthUri: "/health" + custom: true + health: + protocol: HTTP + port: 80 + expectedStatusCode: 200 + timeout: 20 + uri: "/health" + inst_backend: GCP-USCENT1-A + inst_gpu_type: H100 + inst_type: GCP.GPU.H100_1x + inst_min: 1 + inst_max: 1 + containerEnvironment: + - key: SSH_PUBKEY + value: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQCuHBN8zVeYhVa55kXacwJ13/Si/OpWOxpzfVHusVGS+HYpxvxORZJzbRVgwfUygyMgxkW33EVzWNigHLRWb2uA7T8E/fH7sES+4xO0d11yKkUre/8KSAa3X++75p86FQGLnIUrYQZm5kpY6gVZYAb68A/q3bUGrPEouj9amCVwBIJJaQ6AhQiB2vtjhLAgfbLjp+A29MuZYRIzHGljsI1RQgGOBUyeKK6So43MUB4Q+In+Cw1gzjUUv9NJDqbgtg+e9e28UQDU2XhIJEChUvOO0j9lBS4MgcA+K9btvX7sdQIriRuWaUYsrtXL6CbxIjZgclKuFUmMogUUWU7cLraavvDODzuFpNly1IwVVDYBgIAlQHsSOXkACwQigthECoM7qMBR29+xLcMrTD2YkjJJo/QVzCzcKqcqCn+0/8Es0MXGV7HQvEKLZW625hJ76twnFNOXuykJc7WlPbvqdWzBJBH8PDZDBoiQ1W9+pMK6qP3eB4JK6HXMKrtJEIQ28ZxiOgHFWsvUrlRD2VKPFzBOH6ZQe8mItsyy64zSCk3XyLLrLktq8gnOY9VOci6ukLKyLoS5LulzUjWVmTpeiJY2g0Frim+ZpJSIO24LW9Z+5fEpf3935xNGeCyWeO2Is/vY+6dEpDEV+sey2XrUe7nTw5xtfsemSwJTO5zmtjuWzQ== tom@brev.dev" + - key: CLOUDFLARE_TOKEN + value: "eyJhIjoiYjI5N2IzZTNiZmVlNGJlYTA0ZGZhNGI3ZTJmMzEwYTQiLCJ0IjoiNzljNWI0MzAtMWFlYS00NmNmLWE1NzItMGVmN2MxNDdmMDZmIiwicyI6ImJTQzU0UWpUMFZOQUQ2eGRsYVZrbVA1L012S1NoaENIR01vQ3BkZ29zWEU9In0="