Repository: caddyserver/caddy
File: caddy.go (Go)

Code before:
			// unmarshal it again because it's likely that
			// pointers deep in our rawCfg map were modified
			err2 := rollbackRawCfg()
			if err2 != nil {
				err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
			}
		}

		return fmt.Errorf("loading new config: %v", err)
	}

	// success, so update our stored copy of the encoded
	// config to keep it consistent with what caddy is now
	// running (storing an encoded copy is not strictly
	// necessary, but avoids an extra json.Marshal for
	// each config change)
	rawCfgJSON = newCfg
	rawCfgIndex = idx

	return nil
}

func rollbackRawCfg() error {
	var oldCfg any
	err := json.Unmarshal(rawCfgJSON, &oldCfg)
	rawCfg[rawConfigKey] = oldCfg
	return err
}

// readConfig traverses the current config to path
// and writes its JSON encoding to out.
func readConfig(path string, out io.Writer) error {
	rawCfgMu.RLock()
	defer rawCfgMu.RUnlock()
	return unsyncedConfigAccess(http.MethodGet, path, nil, out)
}
func writeToIdIndex(index map[string]string, key, val string) error {
	_, found := index[key]
	if found {
		return errors.New("multiple keys found: " + key)
	}
	index[key] = val
	return nil
}

// indexConfigObjects recursively searches ptr for object fields named
// "@id" and maps that ID value to the full configPath in the index.
// This function is NOT safe for concurrent access; obtain a write lock
// on currentCtxMu.
func indexConfigObjects(ptr any, configPath string, index map[string]string) error {
	switch val := ptr.(type) {

Review comment (suggestion):
obs: Potential nil assignment to `rawCfg`. Check for error before assigning to the map

Code after:
	}

	// load this new config; if it fails, we need to revert to
	// our old representation of caddy's actual config
	err = unsyncedDecodeAndRun(newCfg, true)
	if err != nil {
		if len(rawCfgJSON) > 0 {
			// restore old config state to keep it consistent
			// with what caddy is still running; we need to
			// unmarshal it again because it's likely that
			// pointers deep in our rawCfg map were modified
			var oldCfg any
			err2 := json.Unmarshal(rawCfgJSON, &oldCfg)
			if err2 != nil {
				err = fmt.Errorf("%v; additionally, restoring old config: %v", err, err2)
			}
			rawCfg[rawConfigKey] = oldCfg
		} else {
			rawCfg[rawConfigKey] = nil
		}

		return fmt.Errorf("loading new config: %v", err)
	}

	// success, so update our stored copy of the encoded
	// config to keep it consistent with what caddy is now
	// running (storing an encoded copy is not strictly
	// necessary, but avoids an extra json.Marshal for
	// each config change)
	rawCfgJSON = newCfg
	rawCfgIndex = idx

	return nil
}

// readConfig traverses the current config to path
// and writes its JSON encoding to out.
func readConfig(path string, out io.Writer) error {
	rawCfgMu.RLock()
	defer rawCfgMu.RUnlock()
	return unsyncedConfigAccess(http.MethodGet, path, nil, out)
}

// indexConfigObjects recursively searches ptr for object fields named
// "@id" and maps that ID value to the full configPath in the index.
// This function is NOT safe for concurrent access; obtain a write lock
// on currentCtxMu.
func indexConfigObjects(ptr any, configPath string, index map[string]string) error {
	switch val := ptr.(type) {
	case map[string]any:
		for k, v := range val {