Skip to content

Commit 6a2ec45

Browse files
smarthallclaude
andcommitted
Add /config endpoint integration with lazy loading
Implements client-side integration for the new /config API endpoint that allows backplane-cli to fetch server-managed configuration values. Features: - Refresh command: 'ocm backplane config refresh' to force update server values - Backwards compatible: Existing config files work without changes Implementation: - New ConfigAPIClient interface for fetching remote config - New refresh command to manually update server-managed values Server-managed values: - jira-base-url - assume-initial-arn - prod-env-name - jira-config-for-access-requests Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 48d9d4f commit 6a2ec45

13 files changed

Lines changed: 1236 additions & 10 deletions

File tree

cmd/ocm-backplane/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ govcloud Set to true if used in FedRAMP
3232
cmd.AddCommand(newGetCmd())
3333
cmd.AddCommand(newSetCmd())
3434
cmd.AddCommand(newTroubleshootCmd())
35+
cmd.AddCommand(newRefreshCmd())
3536
return cmd
3637
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package config
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/spf13/cobra"
9+
"github.com/spf13/viper"
10+
11+
BackplaneApi "github.com/openshift/backplane-api/pkg/client"
12+
"github.com/openshift/backplane-cli/pkg/cli/config"
13+
"github.com/openshift/backplane-cli/pkg/info"
14+
"github.com/openshift/backplane-cli/pkg/ocm"
15+
logger "github.com/sirupsen/logrus"
16+
)
17+
18+
func newRefreshCmd() *cobra.Command {
19+
cmd := &cobra.Command{
20+
Use: "refresh",
21+
Short: "Refresh configuration from backplane-api server",
22+
Long: `Fetch the latest configuration values from the backplane-api server and update the local config file.
23+
24+
This command will overwrite the following server-managed configuration values:
25+
- jira-base-url
26+
- assume-initial-arn
27+
- prod-env-name
28+
- jira-config-for-access-requests
29+
30+
User-defined values (proxy-url, session-dir, etc.) will not be modified.`,
31+
SilenceUsage: true,
32+
Args: cobra.NoArgs,
33+
RunE: runRefresh,
34+
}
35+
36+
return cmd
37+
}
38+
39+
func runRefresh(cmd *cobra.Command, args []string) error {
40+
logger.Info("Refreshing configuration from backplane-api...")
41+
42+
// Get current config to determine backplane URL
43+
bpConfig, err := config.GetBackplaneConfiguration()
44+
if err != nil {
45+
return fmt.Errorf("failed to load current configuration: %w", err)
46+
}
47+
48+
// Get OCM access token
49+
token, err := ocm.DefaultOCMInterface.GetOCMAccessToken()
50+
if err != nil {
51+
return fmt.Errorf("failed to get OCM access token: %w", err)
52+
}
53+
54+
// Create backplane API client
55+
client, err := BackplaneApi.NewClient(bpConfig.URL, func(c *BackplaneApi.Client) error {
56+
c.RequestEditors = append(c.RequestEditors, func(ctx context.Context, req *http.Request) error {
57+
req.Header.Add("Authorization", "Bearer "+*token)
58+
req.Header.Set("User-Agent", "backplane-cli"+info.Version)
59+
req.Header.Set("Backplane-Version", info.DefaultInfoService.GetVersion())
60+
return nil
61+
})
62+
return nil
63+
})
64+
if err != nil {
65+
return fmt.Errorf("failed to create backplane API client: %w", err)
66+
}
67+
68+
// Fetch fresh config from API
69+
remoteConfig, err := config.DefaultAPIClient.FetchRemoteConfig(client)
70+
if err != nil {
71+
return fmt.Errorf("failed to fetch configuration from server: %w", err)
72+
}
73+
74+
// Get config file path
75+
configPath, err := config.GetConfigFilePath()
76+
if err != nil {
77+
return fmt.Errorf("failed to get config file path: %w", err)
78+
}
79+
80+
// Reset viper to clear any defaults that were set by GetBackplaneConfiguration
81+
// This prevents defaults like 'govcloud' from leaking into the user's config file
82+
viper.Reset()
83+
84+
// Load existing config file to preserve user settings
85+
viper.SetConfigFile(configPath)
86+
viper.SetConfigType("json")
87+
if err := viper.ReadInConfig(); err != nil {
88+
// If config doesn't exist, that's okay - we'll create it
89+
logger.Debugf("No existing config file found, will create new one")
90+
}
91+
92+
// Update only server-managed values
93+
updated := false
94+
if remoteConfig.JiraBaseURL != nil {
95+
viper.Set(config.JiraBaseURLKey, *remoteConfig.JiraBaseURL)
96+
logger.Infof("Updated jira-base-url: %s", *remoteConfig.JiraBaseURL)
97+
updated = true
98+
}
99+
100+
if remoteConfig.AssumeInitialArn != nil {
101+
viper.Set(config.AssumeInitialArnKey, *remoteConfig.AssumeInitialArn)
102+
logger.Infof("Updated assume-initial-arn: %s", *remoteConfig.AssumeInitialArn)
103+
updated = true
104+
}
105+
106+
if remoteConfig.ProdEnvName != nil {
107+
viper.Set(config.ProdEnvNameKey, *remoteConfig.ProdEnvName)
108+
logger.Infof("Updated prod-env-name: %s", *remoteConfig.ProdEnvName)
109+
updated = true
110+
}
111+
112+
if remoteConfig.JiraConfigForAccessRequests != nil {
113+
viper.Set(config.JiraConfigForAccessRequestsKey, remoteConfig.JiraConfigForAccessRequests)
114+
logger.Info("Updated jira-config-for-access-requests")
115+
updated = true
116+
}
117+
118+
if !updated {
119+
logger.Warn("No configuration values were returned from the server")
120+
}
121+
122+
// Write updated config to file
123+
if err := viper.WriteConfigAs(configPath); err != nil {
124+
return fmt.Errorf("failed to write config file: %w", err)
125+
}
126+
127+
fmt.Printf("Configuration refreshed successfully and saved to %s\n", configPath)
128+
return nil
129+
}

0 commit comments

Comments
 (0)