Skip to content

Commit c9be979

Browse files
edwardrfedw-defang
andauthored
Use fabric to save default provider for the project (#853)
* Use provider from compose file or saved local state file * Use fabric to save default provider for project * Make provider an enum in grpc and use loader.LoadProjectName --------- Co-authored-by: Edward J <[email protected]>
1 parent ee6549a commit c9be979

File tree

15 files changed

+1640
-1279
lines changed

15 files changed

+1640
-1279
lines changed

src/cmd/cli/command/commands.go

Lines changed: 86 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func Execute(ctx context.Context) error {
9191

9292
if strings.Contains(err.Error(), "maximum number of projects") {
9393
projectName := "<name>"
94-
provider, err := getProvider(ctx)
94+
provider, err := getProvider(ctx, nil)
9595
if err != nil {
9696
return err
9797
}
@@ -385,7 +385,8 @@ var whoamiCmd = &cobra.Command{
385385
Args: cobra.NoArgs,
386386
Short: "Show the current user",
387387
RunE: func(cmd *cobra.Command, args []string) error {
388-
provider, err := getProvider(cmd.Context())
388+
loader := configureLoader(cmd)
389+
provider, err := getProvider(cmd.Context(), loader)
389390
if err != nil {
390391
return err
391392
}
@@ -412,7 +413,7 @@ var certGenerateCmd = &cobra.Command{
412413
Short: "Generate a TLS certificate",
413414
RunE: func(cmd *cobra.Command, args []string) error {
414415
loader := configureLoader(cmd)
415-
provider, err := getProvider(cmd.Context())
416+
provider, err := getProvider(cmd.Context(), loader)
416417
if err != nil {
417418
return err
418419
}
@@ -660,7 +661,7 @@ var configSetCmd = &cobra.Command{
660661

661662
// Make sure we have a project to set config for before asking for a value
662663
loader := configureLoader(cmd)
663-
provider, err := getProvider(cmd.Context())
664+
provider, err := getProvider(cmd.Context(), loader)
664665
if err != nil {
665666
return err
666667
}
@@ -737,7 +738,7 @@ var configDeleteCmd = &cobra.Command{
737738
Short: "Removes one or more config values",
738739
RunE: func(cmd *cobra.Command, names []string) error {
739740
loader := configureLoader(cmd)
740-
provider, err := getProvider(cmd.Context())
741+
provider, err := getProvider(cmd.Context(), loader)
741742
if err != nil {
742743
return err
743744
}
@@ -764,7 +765,7 @@ var configListCmd = &cobra.Command{
764765
Short: "List configs",
765766
RunE: func(cmd *cobra.Command, args []string) error {
766767
loader := configureLoader(cmd)
767-
provider, err := getProvider(cmd.Context())
768+
provider, err := getProvider(cmd.Context(), loader)
768769
if err != nil {
769770
return err
770771
}
@@ -781,7 +782,7 @@ var debugCmd = &cobra.Command{
781782
etag, _ := cmd.Flags().GetString("etag")
782783

783784
loader := configureLoader(cmd)
784-
provider, err := getProvider(cmd.Context())
785+
provider, err := getProvider(cmd.Context(), loader)
785786
if err != nil {
786787
return err
787788
}
@@ -800,7 +801,7 @@ var deleteCmd = &cobra.Command{
800801
var tail, _ = cmd.Flags().GetBool("tail")
801802

802803
loader := configureLoader(cmd)
803-
provider, err := getProvider(cmd.Context())
804+
provider, err := getProvider(cmd.Context(), loader)
804805
if err != nil {
805806
return err
806807
}
@@ -902,7 +903,7 @@ var cdDestroyCmd = &cobra.Command{
902903
Short: "Destroy the service stack",
903904
RunE: func(cmd *cobra.Command, args []string) error {
904905
loader := configureLoader(cmd)
905-
provider, err := getProvider(cmd.Context())
906+
provider, err := getProvider(cmd.Context(), loader)
906907
if err != nil {
907908
return err
908909
}
@@ -916,7 +917,7 @@ var cdDownCmd = &cobra.Command{
916917
Short: "Refresh and then destroy the service stack",
917918
RunE: func(cmd *cobra.Command, args []string) error {
918919
loader := configureLoader(cmd)
919-
provider, err := getProvider(cmd.Context())
920+
provider, err := getProvider(cmd.Context(), loader)
920921
if err != nil {
921922
return err
922923
}
@@ -930,7 +931,7 @@ var cdRefreshCmd = &cobra.Command{
930931
Short: "Refresh the service stack",
931932
RunE: func(cmd *cobra.Command, args []string) error {
932933
loader := configureLoader(cmd)
933-
provider, err := getProvider(cmd.Context())
934+
provider, err := getProvider(cmd.Context(), loader)
934935
if err != nil {
935936
return err
936937
}
@@ -944,7 +945,7 @@ var cdCancelCmd = &cobra.Command{
944945
Short: "Cancel the current CD operation",
945946
RunE: func(cmd *cobra.Command, args []string) error {
946947
loader := configureLoader(cmd)
947-
provider, err := getProvider(cmd.Context())
948+
provider, err := getProvider(cmd.Context(), loader)
948949
if err != nil {
949950
return err
950951
}
@@ -959,7 +960,8 @@ var cdTearDownCmd = &cobra.Command{
959960
RunE: func(cmd *cobra.Command, args []string) error {
960961
force, _ := cmd.Flags().GetBool("force")
961962

962-
provider, err := getProvider(cmd.Context())
963+
loader := configureLoader(cmd)
964+
provider, err := getProvider(cmd.Context(), loader)
963965
if err != nil {
964966
return err
965967
}
@@ -976,7 +978,7 @@ var cdListCmd = &cobra.Command{
976978
remote, _ := cmd.Flags().GetBool("remote")
977979

978980
loader := configureLoader(cmd)
979-
provider, err := getProvider(cmd.Context())
981+
provider, err := getProvider(cmd.Context(), loader)
980982
if err != nil {
981983
return err
982984
}
@@ -994,7 +996,7 @@ var cdPreviewCmd = &cobra.Command{
994996
Short: "Preview the changes that will be made by the CD task",
995997
RunE: func(cmd *cobra.Command, args []string) error {
996998
loader := configureLoader(cmd)
997-
provider, err := getProvider(cmd.Context())
999+
provider, err := getProvider(cmd.Context(), loader)
9981000
if err != nil {
9991001
return err
10001002
}
@@ -1076,30 +1078,29 @@ var providerDescription = map[cliClient.ProviderID]string{
10761078
cliClient.ProviderDO: "Deploy to DigitalOcean using the DIGITALOCEAN_TOKEN, SPACES_ACCESS_KEY_ID, and SPACES_SECRET_ACCESS_KEY environment variables.",
10771079
}
10781080

1079-
func getProvider(ctx context.Context) (cliClient.Provider, error) {
1081+
func getProvider(ctx context.Context, loader *compose.Loader) (cliClient.Provider, error) {
1082+
extraMsg := ""
1083+
source := ""
1084+
1085+
if val, ok := os.LookupEnv("DEFANG_PROVIDER"); ok && val == providerID.String() {
1086+
// Sanitize the provider value from the environment variable
1087+
if err := providerID.Set(val); err != nil {
1088+
return nil, fmt.Errorf("invalid provider '%v' in environment variable DEFANG_PROVIDER, supported providers are: %v", val, cliClient.AllProviders())
1089+
}
1090+
source = "environment variable"
1091+
}
1092+
1093+
if RootCmd.PersistentFlags().Changed("provider") {
1094+
source = "command line flag"
1095+
}
1096+
10801097
switch providerID {
10811098
case cliClient.ProviderAuto:
10821099
if !nonInteractive {
1083-
// Prompt the user to choose a provider if in interactive mode
1084-
options := []string{}
1085-
for _, p := range cliClient.AllProviders() {
1086-
options = append(options, p.String())
1087-
}
1088-
var optionValue string
1089-
if err := survey.AskOne(&survey.Select{
1090-
Message: "Choose a cloud provider:",
1091-
Options: options,
1092-
Help: "The provider you choose will be used for deploying services.",
1093-
Description: func(value string, i int) string {
1094-
return providerDescription[cliClient.ProviderID(value)]
1095-
},
1096-
}, &optionValue); err != nil {
1100+
var err error
1101+
if source, err = determineProviderID(ctx, loader); err != nil {
10971102
return nil, err
10981103
}
1099-
if err := providerID.Set(optionValue); err != nil {
1100-
panic(err)
1101-
}
1102-
term.Printf("To skip this prompt, set the DEFANG_PROVIDER=%s in your environment, or use:\n\n defang --provider=%s\n\n", optionValue, optionValue)
11031104
} else {
11041105
// Defaults to defang provider in non-interactive mode
11051106
if awsInEnv() {
@@ -1120,8 +1121,58 @@ func getProvider(ctx context.Context) (cliClient.Provider, error) {
11201121
}
11211122
case cliClient.ProviderDefang:
11221123
// Ignore any env vars when explicitly using the Defang playground provider
1124+
extraMsg = "; consider using BYOC (https://s.defang.io/byoc)"
11231125
}
11241126

1125-
provider := cli.NewProvider(ctx, providerID, client)
1127+
term.Infof("Using %s provider from %s%s", providerID.Name(), source, extraMsg)
1128+
provider, err := cli.NewProvider(ctx, providerID, client)
1129+
if err != nil {
1130+
return nil, err
1131+
}
11261132
return provider, nil
11271133
}
1134+
1135+
func determineProviderID(ctx context.Context, loader *compose.Loader) (string, error) {
1136+
projName, err := loader.LoadProjectName(ctx)
1137+
if err != nil {
1138+
term.Warn("Unable to load project:", err)
1139+
} else if !RootCmd.PersistentFlags().Changed("provider") { // If user manually selected auto provider, do not load from remote
1140+
resp, err := client.GetSelectedProvider(ctx, &defangv1.GetSelectedProviderRequest{Project: projName})
1141+
if err != nil {
1142+
term.Warn("Unable to get selected provider:", err)
1143+
} else if resp.Provider != defangv1.Provider_PROVIDER_UNSPECIFIED {
1144+
providerID.SetEnumValue(resp.Provider)
1145+
return "defang server", nil
1146+
}
1147+
}
1148+
1149+
// Prompt the user to choose a provider if in interactive mode
1150+
options := []string{}
1151+
for _, p := range cliClient.AllProviders() {
1152+
options = append(options, p.String())
1153+
}
1154+
var optionValue string
1155+
if err := survey.AskOne(&survey.Select{
1156+
Message: "Choose a cloud provider:",
1157+
Options: options,
1158+
Help: "The provider you choose will be used for deploying services.",
1159+
Description: func(value string, i int) string {
1160+
return providerDescription[cliClient.ProviderID(value)]
1161+
},
1162+
}, &optionValue); err != nil {
1163+
return "", err
1164+
}
1165+
if err := providerID.Set(optionValue); err != nil {
1166+
panic(err)
1167+
}
1168+
1169+
// Save the selected provider to the fabric
1170+
if projName != "" {
1171+
if err := client.SetSelectedProvider(ctx, &defangv1.SetSelectedProviderRequest{Project: projName, Provider: providerID.EnumValue()}); err != nil {
1172+
term.Warn("Unable to save selected provider to defang server:", err)
1173+
} else {
1174+
term.Printf("%v is now the default provider for project %v and will auto-select next time if no other provider is specified. Use --provider=auto to reselect.", providerID, projName)
1175+
}
1176+
}
1177+
return "interactive prompt", nil
1178+
}

src/cmd/cli/command/compose.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ func makeComposeUpCmd() *cobra.Command {
5959

6060
since := time.Now()
6161
loader := configureLoader(cmd)
62-
provider, err := getProvider(cmd.Context())
62+
provider, err := getProvider(cmd.Context(), loader)
6363
if err != nil {
6464
return err
6565
}
@@ -254,7 +254,7 @@ func makeComposeDownCmd() *cobra.Command {
254254
var detach, _ = cmd.Flags().GetBool("detach")
255255

256256
loader := configureLoader(cmd)
257-
provider, err := getProvider(cmd.Context())
257+
provider, err := getProvider(cmd.Context(), loader)
258258
if err != nil {
259259
return err
260260
}
@@ -311,7 +311,7 @@ func makeComposeConfigCmd() *cobra.Command {
311311
Short: "Reads a Compose file and shows the generated config",
312312
RunE: func(cmd *cobra.Command, args []string) error {
313313
loader := configureLoader(cmd)
314-
provider, err := getProvider(cmd.Context())
314+
provider, err := getProvider(cmd.Context(), loader)
315315
if err != nil {
316316
return err
317317
}
@@ -334,7 +334,7 @@ func makeComposeLsCmd() *cobra.Command {
334334
long, _ := cmd.Flags().GetBool("long")
335335

336336
loader := configureLoader(cmd)
337-
provider, err := getProvider(cmd.Context())
337+
provider, err := getProvider(cmd.Context(), loader)
338338
if err != nil {
339339
return err
340340
}
@@ -407,7 +407,7 @@ func makeComposeLogsCmd() *cobra.Command {
407407
}
408408

409409
loader := configureLoader(cmd)
410-
provider, err := getProvider(cmd.Context())
410+
provider, err := getProvider(cmd.Context(), loader)
411411
if err != nil {
412412
return err
413413
}

0 commit comments

Comments
 (0)