1package autoconf
2
3import (
4	"fmt"
5	"io/ioutil"
6	"os"
7	"path/filepath"
8	"strings"
9
10	"github.com/golang/protobuf/jsonpb"
11	"github.com/hashicorp/consul/proto/pbautoconf"
12)
13
14const (
15	// autoConfigFileName is the name of the file that the agent auto-config settings are
16	// stored in within the data directory
17	autoConfigFileName = "auto-config.json"
18)
19
20var (
21	pbMarshaler = &jsonpb.Marshaler{
22		OrigName:     false,
23		EnumsAsInts:  false,
24		Indent:       "   ",
25		EmitDefaults: true,
26	}
27
28	pbUnmarshaler = &jsonpb.Unmarshaler{
29		AllowUnknownFields: false,
30	}
31)
32
33func (ac *AutoConfig) readPersistedAutoConfig() (*pbautoconf.AutoConfigResponse, error) {
34	if ac.config.DataDir == "" {
35		// no data directory means we don't have anything to potentially load
36		return nil, nil
37	}
38
39	path := filepath.Join(ac.config.DataDir, autoConfigFileName)
40	ac.logger.Debug("attempting to restore any persisted configuration", "path", path)
41
42	content, err := ioutil.ReadFile(path)
43	if err == nil {
44		rdr := strings.NewReader(string(content))
45
46		var resp pbautoconf.AutoConfigResponse
47		if err := pbUnmarshaler.Unmarshal(rdr, &resp); err != nil {
48			return nil, fmt.Errorf("failed to decode persisted auto-config data: %w", err)
49		}
50
51		ac.logger.Info("read persisted configuration", "path", path)
52		return &resp, nil
53	}
54
55	if !os.IsNotExist(err) {
56		return nil, fmt.Errorf("failed to load %s: %w", path, err)
57	}
58
59	// ignore non-existence errors as that is an indicator that we haven't
60	// performed the auto configuration before
61	return nil, nil
62}
63
64func (ac *AutoConfig) persistAutoConfig(resp *pbautoconf.AutoConfigResponse) error {
65	// now that we know the configuration is generally fine including TLS certs go ahead and persist it to disk.
66	if ac.config.DataDir == "" {
67		ac.logger.Debug("not persisting auto-config settings because there is no data directory")
68		return nil
69	}
70
71	serialized, err := pbMarshaler.MarshalToString(resp)
72	if err != nil {
73		return fmt.Errorf("failed to encode auto-config response as JSON: %w", err)
74	}
75
76	path := filepath.Join(ac.config.DataDir, autoConfigFileName)
77
78	err = ioutil.WriteFile(path, []byte(serialized), 0660)
79	if err != nil {
80		return fmt.Errorf("failed to write auto-config configurations: %w", err)
81	}
82
83	ac.logger.Debug("auto-config settings were persisted to disk")
84
85	return nil
86}
87