crossplane

Форк
0
145 строк · 3.9 Кб
1
/*
2
Copyright 2023 The Crossplane Authors.
3

4
Licensed under the Apache License, Version 2.0 (the "License");
5
you may not use this file except in compliance with the License.
6
You may obtain a copy of the License at
7

8
    http://www.apache.org/licenses/LICENSE-2.0
9

10
Unless required by applicable law or agreed to in writing, software
11
distributed under the License is distributed on an "AS IS" BASIS,
12
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
See the License for the specific language governing permissions and
14
limitations under the License.
15
*/
16

17
package config
18

19
import (
20
	"encoding/json"
21
	"io"
22
	"os"
23
	"path/filepath"
24

25
	"github.com/spf13/afero"
26
)
27

28
// Source is a source for interacting with a Config.
29
type Source interface {
30
	Initialize() error
31
	GetConfig() (*Config, error)
32
	UpdateConfig(*Config) error
33
}
34

35
// NewFSSource constructs a new FSSource. Path must be supplied via modifier or
36
// Initialize must be called to use default.
37
// NOTE(hasheddan): using empty path by default is a bit of a footgun, so we
38
// should consider refactoring here. The motivation for the current design is to
39
// allow for flexibility in cases where the consumer does not want to create if
40
// the path does not exist, or they want to provide the FSSource as the default
41
// without handling an error in construction (see Docker credential helper for
42
// example).
43
func NewFSSource(modifiers ...FSSourceModifier) *FSSource {
44
	src := &FSSource{
45
		fs: afero.NewOsFs(),
46
	}
47
	for _, m := range modifiers {
48
		m(src)
49
	}
50

51
	return src
52
}
53

54
// FSSourceModifier modifies an FSSource.
55
type FSSourceModifier func(*FSSource)
56

57
// WithPath sets the config path for the filesystem source.
58
func WithPath(p string) FSSourceModifier {
59
	return func(f *FSSource) {
60
		f.path = filepath.Clean(p)
61
	}
62
}
63

64
// WithFS overrides the FSSource filesystem with the given filesystem.
65
func WithFS(fs afero.Fs) FSSourceModifier {
66
	return func(f *FSSource) {
67
		f.fs = fs
68
	}
69
}
70

71
// FSSource provides a filesystem source for interacting with a Config.
72
type FSSource struct {
73
	fs   afero.Fs
74
	path string
75
}
76

77
// Initialize creates a config in the filesystem if one does not exist. If path
78
// is not defined the default path is constructed.
79
func (src *FSSource) Initialize() error {
80
	if src.path == "" {
81
		p, err := GetDefaultPath()
82
		if err != nil {
83
			return err
84
		}
85
		src.path = p
86
	}
87
	if _, err := src.fs.Stat(src.path); err != nil {
88
		if !os.IsNotExist(err) {
89
			return err
90
		}
91
		if err := src.fs.MkdirAll(filepath.Dir(src.path), 0755); err != nil {
92
			return err
93
		}
94
		f, err := src.fs.OpenFile(src.path, os.O_CREATE, 0600)
95
		if err != nil {
96
			return err
97
		}
98
		defer f.Close() //nolint:errcheck // we don't care about the error
99
	}
100
	return nil
101
}
102

103
// GetConfig fetches the config from a filesystem.
104
func (src *FSSource) GetConfig() (*Config, error) {
105
	f, err := src.fs.Open(src.path)
106
	if err != nil {
107
		return nil, err
108
	}
109
	defer f.Close() //nolint:errcheck // we don't care about the error
110
	b, err := io.ReadAll(f)
111
	if err != nil {
112
		return nil, err
113
	}
114
	conf := &Config{}
115
	if len(b) == 0 {
116
		return conf, nil
117
	}
118
	if err := json.Unmarshal(b, conf); err != nil {
119
		return nil, err
120
	}
121
	return conf, nil
122
}
123

124
// UpdateConfig updates the Config in the filesystem.
125
func (src *FSSource) UpdateConfig(c *Config) error {
126
	f, err := src.fs.OpenFile(src.path, os.O_RDWR|os.O_TRUNC, 0600)
127
	if err != nil {
128
		return err
129
	}
130
	// NOTE(hasheddan): We both defer and explicitly call Close() to ensure that
131
	// we close the file in the case that we encounter an error before write,
132
	// and that we return an error in the case that we write and then fail to
133
	// close the file (i.e. write buffer is not flushed). In the latter case the
134
	// deferred Close() will error (see https://golang.org/pkg/os/#File.Close),
135
	// but we do not check it.
136
	defer f.Close() //nolint:errcheck // we don't care about the error
137
	b, err := json.MarshalIndent(c, "", "\t")
138
	if err != nil {
139
		return err
140
	}
141
	if _, err := f.Write(b); err != nil {
142
		return err
143
	}
144
	return f.Close()
145
}
146

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.