-
Notifications
You must be signed in to change notification settings - Fork 5.4k
/
discovery.go
204 lines (180 loc) · 7.26 KB
/
discovery.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package discovery
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/golang/protobuf/ptypes/empty"
"github.com/argoproj/argo-cd/v2/util/io/files"
grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
log "github.com/sirupsen/logrus"
pluginclient "github.com/argoproj/argo-cd/v2/cmpserver/apiclient"
"github.com/argoproj/argo-cd/v2/common"
"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
"github.com/argoproj/argo-cd/v2/util/cmp"
"github.com/argoproj/argo-cd/v2/util/io"
"github.com/argoproj/argo-cd/v2/util/kustomize"
)
func IsManifestGenerationEnabled(sourceType v1alpha1.ApplicationSourceType, enableGenerateManifests map[string]bool) bool {
if enableGenerateManifests == nil {
return true
}
enabled, ok := enableGenerateManifests[string(sourceType)]
if !ok {
return true
}
return enabled
}
func Discover(ctx context.Context, appPath, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string, env []string) (map[string]string, error) {
apps := make(map[string]string)
// Check if it is CMP
conn, _, err := DetectConfigManagementPlugin(ctx, appPath, repoPath, "", env, tarExcludedGlobs)
if err == nil {
// Found CMP
io.Close(conn)
apps["."] = string(v1alpha1.ApplicationSourceTypePlugin)
return apps, nil
}
err = filepath.Walk(appPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
dir, err := filepath.Rel(appPath, filepath.Dir(path))
if err != nil {
return err
}
base := filepath.Base(path)
if strings.HasSuffix(base, "Chart.yaml") && IsManifestGenerationEnabled(v1alpha1.ApplicationSourceTypeHelm, enableGenerateManifests) {
apps[dir] = string(v1alpha1.ApplicationSourceTypeHelm)
}
if kustomize.IsKustomization(base) && IsManifestGenerationEnabled(v1alpha1.ApplicationSourceTypeKustomize, enableGenerateManifests) {
apps[dir] = string(v1alpha1.ApplicationSourceTypeKustomize)
}
return nil
})
return apps, err
}
func AppType(ctx context.Context, appPath, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string, env []string) (string, error) {
apps, err := Discover(ctx, appPath, repoPath, enableGenerateManifests, tarExcludedGlobs, env)
if err != nil {
return "", err
}
appType, ok := apps["."]
if ok {
return appType, nil
}
return "Directory", nil
}
// if pluginName is provided setup connection to that cmp-server
// else
// list all plugins in /plugins folder and foreach plugin
// check cmpSupports()
// if supported return conn for the cmp-server
func DetectConfigManagementPlugin(ctx context.Context, appPath, repoPath, pluginName string, env []string, tarExcludedGlobs []string) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, error) {
var conn io.Closer
var cmpClient pluginclient.ConfigManagementPluginServiceClient
var connFound bool
pluginSockFilePath := common.GetPluginSockFilePath()
log.WithFields(log.Fields{
common.SecurityField: common.SecurityLow,
common.SecurityCWEField: common.SecurityCWEMissingReleaseOfFileDescriptor,
}).Debugf("pluginSockFilePath is: %s", pluginSockFilePath)
if pluginName != "" {
// check if the given plugin supports the repo
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, appPath, repoPath, fmt.Sprintf("%v.sock", pluginName), env, tarExcludedGlobs, true)
if !connFound {
return nil, nil, fmt.Errorf("couldn't find cmp-server plugin with name %q supporting the given repository", pluginName)
}
} else {
fileList, err := os.ReadDir(pluginSockFilePath)
if err != nil {
return nil, nil, fmt.Errorf("Failed to list all plugins in dir, error=%w", err)
}
for _, file := range fileList {
if file.Type() == os.ModeSocket {
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, appPath, repoPath, file.Name(), env, tarExcludedGlobs, false)
if connFound {
break
}
}
}
if !connFound {
return nil, nil, fmt.Errorf("could not find plugin supporting the given repository")
}
}
return conn, cmpClient, nil
}
// matchRepositoryCMP will send the repoPath to the cmp-server. The cmp-server will
// inspect the files and return true if the repo is supported for manifest generation.
// Will return false otherwise.
func matchRepositoryCMP(ctx context.Context, appPath, repoPath string, client pluginclient.ConfigManagementPluginServiceClient, env []string, tarExcludedGlobs []string) (bool, bool, error) {
matchRepoStream, err := client.MatchRepository(ctx, grpc_retry.Disable())
if err != nil {
return false, false, fmt.Errorf("error getting stream client: %w", err)
}
err = cmp.SendRepoStream(ctx, appPath, repoPath, matchRepoStream, env, tarExcludedGlobs)
if err != nil {
return false, false, fmt.Errorf("error sending stream: %w", err)
}
resp, err := matchRepoStream.CloseAndRecv()
if err != nil {
return false, false, fmt.Errorf("error receiving stream response: %w", err)
}
return resp.GetIsSupported(), resp.GetIsDiscoveryEnabled(), nil
}
func cmpSupports(ctx context.Context, pluginSockFilePath, appPath, repoPath, fileName string, env []string, tarExcludedGlobs []string, namedPlugin bool) (io.Closer, pluginclient.ConfigManagementPluginServiceClient, bool) {
absPluginSockFilePath, err := filepath.Abs(pluginSockFilePath)
if err != nil {
log.Errorf("error getting absolute path for plugin socket dir %v, %v", pluginSockFilePath, err)
return nil, nil, false
}
address := filepath.Join(absPluginSockFilePath, fileName)
if !files.Inbound(address, absPluginSockFilePath) {
log.Errorf("invalid socket file path, %v is outside plugin socket dir %v", fileName, pluginSockFilePath)
return nil, nil, false
}
cmpclientset := pluginclient.NewConfigManagementPluginClientSet(address)
conn, cmpClient, err := cmpclientset.NewConfigManagementPluginClient()
if err != nil {
log.WithFields(log.Fields{
common.SecurityField: common.SecurityMedium,
common.SecurityCWEField: common.SecurityCWEMissingReleaseOfFileDescriptor,
}).Errorf("error dialing to cmp-server for plugin %s, %v", fileName, err)
return nil, nil, false
}
cfg, err := cmpClient.CheckPluginConfiguration(ctx, &empty.Empty{})
if err != nil {
log.Errorf("error checking plugin configuration %s, %v", fileName, err)
return nil, nil, false
}
// if discovery is not configured, return the client without further checks
if !cfg.IsDiscoveryConfigured {
return conn, cmpClient, true
}
isSupported, isDiscoveryEnabled, err := matchRepositoryCMP(ctx, appPath, repoPath, cmpClient, env, tarExcludedGlobs)
if err != nil {
log.WithFields(log.Fields{
common.SecurityField: common.SecurityMedium,
common.SecurityCWEField: common.SecurityCWEMissingReleaseOfFileDescriptor,
}).Errorf("repository %s is not the match because %v", repoPath, err)
io.Close(conn)
return nil, nil, false
}
if !isSupported {
// if discovery is not set and the plugin name is specified, let app use the plugin
if !isDiscoveryEnabled && namedPlugin {
return conn, cmpClient, true
}
log.WithFields(log.Fields{
common.SecurityField: common.SecurityLow,
common.SecurityCWEField: common.SecurityCWEMissingReleaseOfFileDescriptor,
}).Debugf("Response from socket file %s does not support %v", fileName, repoPath)
io.Close(conn)
return nil, nil, false
}
return conn, cmpClient, true
}