argo-cd/util/app/discovery/discovery.go
schakrad faa7331f9d
feat: UI write support CMP (#11754) (#12137)
* #11602 fix : Object options menu truncated when selected in ApplicationListView.

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* CMP parameter changes

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* fix: pointers to param values

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

better?

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

fix silliness

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

terrible hacks

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

maybe better codegen

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

fix tests

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* same prefix issue fixed

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* fix for delete param name

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* lint changes

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* lint fix

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* lint fix

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* finalChanges

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* Delete application-resource-list.tsx

Not needed for this PR.

Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>

* added file which was deleted as it was not the change needed for this feature.

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* refactored MapValuField and added fix for some edge cases

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* Update application-resource-list.tsx

Reverting the change as this is not related to this PR.

Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>

* Reverting the change in application-resource-list

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* Showing application parameter values irrespective of parameter present or not in plugin.yaml

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* fix for lint errors

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>

* fix false source mismatch

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix equals

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* fix swagger doc

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>

* Tooltip description change.

Signed-off-by: schakrad <chakradari.sindhu@gmail.com>

* fixed lint

Signed-off-by: schakrad <chakradari.sindhu@gmail.com>

* CMP fix for empty array.

Signed-off-by: schakrad <chakradari.sindhu@gmail.com>

---------

Signed-off-by: schakradari <saisindhu_chakradari@intuit.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: schakrad <58915923+schakrad@users.noreply.github.com>
Signed-off-by: schakrad <chakradari.sindhu@gmail.com>
Co-authored-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
2023-03-21 14:39:10 -04:00

191 lines
6.6 KiB
Go

package discovery
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"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, repoPath string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (map[string]string, error) {
apps := make(map[string]string)
// Check if it is CMP
conn, _, err := DetectConfigManagementPlugin(ctx, repoPath, "", []string{}, tarExcludedGlobs)
if err == nil {
// Found CMP
io.Close(conn)
apps["."] = string(v1alpha1.ApplicationSourceTypePlugin)
return apps, nil
}
err = filepath.Walk(repoPath, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
dir, err := filepath.Rel(repoPath, 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, path string, enableGenerateManifests map[string]bool, tarExcludedGlobs []string) (string, error) {
apps, err := Discover(ctx, path, enableGenerateManifests, tarExcludedGlobs)
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, 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: 775,
}).Debugf("pluginSockFilePath is: %s", pluginSockFilePath)
if pluginName != "" {
// check if the given plugin supports the repo
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, repoPath, fmt.Sprintf("%v.sock", pluginName), env, tarExcludedGlobs, true)
if !connFound {
return nil, nil, fmt.Errorf("couldn't find cmp-server plugin with name %v 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, 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, 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: %s", err)
}
err = cmp.SendRepoStream(ctx, repoPath, repoPath, matchRepoStream, env, tarExcludedGlobs)
if err != nil {
return false, false, fmt.Errorf("error sending stream: %s", err)
}
resp, err := matchRepoStream.CloseAndRecv()
if err != nil {
return false, false, fmt.Errorf("error receiving stream response: %s", err)
}
return resp.GetIsSupported(), resp.GetIsDiscoveryEnabled(), nil
}
func cmpSupports(ctx context.Context, pluginSockFilePath, 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: 775,
}).Errorf("error dialing to cmp-server for plugin %s, %v", fileName, err)
return nil, nil, false
}
isSupported, isDiscoveryEnabled, err := matchRepositoryCMP(ctx, repoPath, cmpClient, env, tarExcludedGlobs)
if err != nil {
log.WithFields(log.Fields{
common.SecurityField: common.SecurityMedium,
common.SecurityCWEField: 775,
}).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: 775,
}).Debugf("Reponse from socket file %s does not support %v", fileName, repoPath)
io.Close(conn)
return nil, nil, false
}
return conn, cmpClient, true
}