Skip to content

Commit aaa82d9

Browse files
maximpertsov10zingpd
authored andcommitted
RSDK-6344 Read partial config when getting cached TLS information (#3620)
1 parent bd2b9d9 commit aaa82d9

File tree

3 files changed

+122
-29
lines changed

3 files changed

+122
-29
lines changed

config/config.go

+11
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,17 @@ func (config *Cloud) Validate(path string, fromCloud bool) error {
605605
return nil
606606
}
607607

608+
// ValidateTLS ensures TLS fields are valid.
609+
func (config *Cloud) ValidateTLS(path string) error {
610+
if config.TLSCertificate == "" {
611+
return resource.NewConfigValidationFieldRequiredError(path, "tls_certificate")
612+
}
613+
if config.TLSPrivateKey == "" {
614+
return resource.NewConfigValidationFieldRequiredError(path, "tls_private_key")
615+
}
616+
return nil
617+
}
618+
608619
// LocationSecret describes a location secret that can be used to authenticate to the rdk.
609620
type LocationSecret struct {
610621
ID string `json:"id"`

config/reader.go

+44-29
Original file line numberDiff line numberDiff line change
@@ -225,28 +225,16 @@ func readFromCloud(
225225
return nil, errors.New("expected config to have cloud section")
226226
}
227227

228-
// empty if not cached, since its a separate request, which we check next
229-
tlsCertificate := cfg.Cloud.TLSCertificate
230-
tlsPrivateKey := cfg.Cloud.TLSPrivateKey
228+
tls := tlsConfig{
229+
// both fields are empty if not cached, since its a separate request, which we
230+
// check next
231+
certificate: cfg.Cloud.TLSCertificate,
232+
privateKey: cfg.Cloud.TLSPrivateKey,
233+
}
231234
if !cached {
232-
// get cached certificate data
233-
// read cached config from fs.
234-
// process the config with fromReader() use processed config as cachedConfig to update the cert data.
235-
unproccessedCachedConfig, err := readFromCache(cloudCfg.ID)
236-
if err == nil {
237-
cachedConfig, err := processConfigFromCloud(unproccessedCachedConfig, logger)
238-
if err != nil {
239-
// clear cache
240-
logger.Warn("Detected failure to process the cached config when retrieving TLS config, clearing cache.")
241-
clearCache(cloudCfg.ID)
242-
return nil, err
243-
}
244-
245-
if cachedConfig.Cloud != nil {
246-
tlsCertificate = cachedConfig.Cloud.TLSCertificate
247-
tlsPrivateKey = cachedConfig.Cloud.TLSPrivateKey
248-
}
249-
} else if !os.IsNotExist(err) {
235+
// Try to get TLS information from the cached config (if it exists) even if we
236+
// got a new config from the cloud.
237+
if err := tls.readFromCache(cloudCfg.ID, logger); err != nil {
250238
return nil, err
251239
}
252240
}
@@ -255,7 +243,7 @@ func readFromCloud(
255243
checkForNewCert = true
256244
}
257245

258-
if checkForNewCert || tlsCertificate == "" || tlsPrivateKey == "" {
246+
if checkForNewCert || tls.certificate == "" || tls.privateKey == "" {
259247
logger.Debug("reading tlsCertificate from the cloud")
260248
// Use the SignalingInsecure from the Cloud config returned from the app not the initial config.
261249

@@ -264,13 +252,13 @@ func readFromCloud(
264252
if !errors.Is(err, context.DeadlineExceeded) {
265253
return nil, err
266254
}
267-
if tlsCertificate == "" || tlsPrivateKey == "" {
255+
if tls.certificate == "" || tls.privateKey == "" {
268256
return nil, errors.Wrap(err, "error getting certificate data from cloud; try again later")
269257
}
270258
logger.Warnw("failed to refresh certificate data; using cached for now", "error", err)
271259
} else {
272-
tlsCertificate = certData.TLSCertificate
273-
tlsPrivateKey = certData.TLSPrivateKey
260+
tls.certificate = certData.TLSCertificate
261+
tls.privateKey = certData.TLSPrivateKey
274262
}
275263
}
276264

@@ -291,14 +279,14 @@ func readFromCloud(
291279
to.Cloud.ManagedBy = managedBy
292280
to.Cloud.LocationSecret = locationSecret
293281
to.Cloud.LocationSecrets = locationSecrets
294-
to.Cloud.TLSCertificate = tlsCertificate
295-
to.Cloud.TLSPrivateKey = tlsPrivateKey
282+
to.Cloud.TLSCertificate = tls.certificate
283+
to.Cloud.TLSPrivateKey = tls.privateKey
296284
}
297285

298286
mergeCloudConfig(cfg)
299287
// TODO(RSDK-1960): add more tests around config caching
300-
unprocessedConfig.Cloud.TLSCertificate = tlsCertificate
301-
unprocessedConfig.Cloud.TLSPrivateKey = tlsPrivateKey
288+
unprocessedConfig.Cloud.TLSCertificate = tls.certificate
289+
unprocessedConfig.Cloud.TLSPrivateKey = tls.privateKey
302290

303291
if err := storeToCache(cloudCfg.ID, unprocessedConfig); err != nil {
304292
logger.Errorw("failed to cache config", "error", err)
@@ -307,6 +295,33 @@ func readFromCloud(
307295
return cfg, nil
308296
}
309297

298+
type tlsConfig struct {
299+
certificate string
300+
privateKey string
301+
}
302+
303+
func (tls *tlsConfig) readFromCache(id string, logger logging.Logger) error {
304+
cachedCfg, err := readFromCache(id)
305+
switch {
306+
case os.IsNotExist(err):
307+
logger.Warn("No cached config, using cloud TLS config.")
308+
case err != nil:
309+
return err
310+
case cachedCfg.Cloud == nil:
311+
logger.Warn("Cached config is not a cloud config, using cloud TLS config.")
312+
default:
313+
if err := cachedCfg.Cloud.ValidateTLS("cloud"); err != nil {
314+
logger.Warn("Detected failure to process the cached config when retrieving TLS config, clearing cache.")
315+
clearCache(id)
316+
return err
317+
}
318+
319+
tls.certificate = cachedCfg.Cloud.TLSCertificate
320+
tls.privateKey = cachedCfg.Cloud.TLSPrivateKey
321+
}
322+
return nil
323+
}
324+
310325
// Read reads a config from the given file.
311326
func Read(
312327
ctx context.Context,

config/reader_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,70 @@ func TestProcessConfig(t *testing.T) {
121121
test.That(t, err, test.ShouldBeNil)
122122
test.That(t, *cfg, test.ShouldResemble, unprocessedConfig)
123123
}
124+
125+
func TestReadTLSFromCache(t *testing.T) {
126+
logger := logging.NewTestLogger(t)
127+
ctx := context.Background()
128+
cfg, err := FromReader(ctx, "", strings.NewReader(`{}`), logger)
129+
test.That(t, err, test.ShouldBeNil)
130+
131+
robotPartID := "forCachingTest"
132+
t.Run("no cached config", func(t *testing.T) {
133+
clearCache(robotPartID)
134+
test.That(t, err, test.ShouldBeNil)
135+
136+
tls := tlsConfig{}
137+
err = tls.readFromCache(robotPartID, logger)
138+
test.That(t, err, test.ShouldBeNil)
139+
})
140+
141+
t.Run("cache config without cloud", func(t *testing.T) {
142+
defer clearCache(robotPartID)
143+
cfg.Cloud = nil
144+
145+
err = storeToCache(robotPartID, cfg)
146+
test.That(t, err, test.ShouldBeNil)
147+
148+
tls := tlsConfig{}
149+
err = tls.readFromCache(robotPartID, logger)
150+
test.That(t, err, test.ShouldBeNil)
151+
})
152+
153+
t.Run("invalid cached TLS", func(t *testing.T) {
154+
defer clearCache(robotPartID)
155+
cloud := &Cloud{
156+
TLSPrivateKey: "key",
157+
}
158+
cfg.Cloud = cloud
159+
160+
err = storeToCache(robotPartID, cfg)
161+
test.That(t, err, test.ShouldBeNil)
162+
163+
tls := tlsConfig{}
164+
err = tls.readFromCache(robotPartID, logger)
165+
test.That(t, err, test.ShouldNotBeNil)
166+
167+
_, err = readFromCache(robotPartID)
168+
test.That(t, os.IsNotExist(err), test.ShouldBeTrue)
169+
})
170+
171+
t.Run("valid cached TLS", func(t *testing.T) {
172+
defer clearCache(robotPartID)
173+
cloud := &Cloud{
174+
TLSCertificate: "cert",
175+
TLSPrivateKey: "key",
176+
}
177+
cfg.Cloud = cloud
178+
179+
err = storeToCache(robotPartID, cfg)
180+
test.That(t, err, test.ShouldBeNil)
181+
182+
// the config is missing several fields required to start the robot, but this
183+
// should not prevent us from reading TLS information from it.
184+
_, err = processConfigFromCloud(cfg, logger)
185+
test.That(t, err, test.ShouldNotBeNil)
186+
tls := tlsConfig{}
187+
err = tls.readFromCache(robotPartID, logger)
188+
test.That(t, err, test.ShouldBeNil)
189+
})
190+
}

0 commit comments

Comments
 (0)