Skip to content

Commit

Permalink
feat: plugins as services
Browse files Browse the repository at this point in the history
This commit makes the repo server able to use cmp plugins via kubernetes
services.

It implements #14132 - read that for details

Signed-off-by: Alan Clucas <alan@clucas.org>
  • Loading branch information
Joibel committed Jan 16, 2024
1 parent d9df252 commit 782c4e7
Show file tree
Hide file tree
Showing 25 changed files with 825 additions and 165 deletions.
1 change: 1 addition & 0 deletions USERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Currently, the following organizations are **officially** using Argo CD:
1. [PGS](https://www.pgs.com)
1. [Pigment](https://www.gopigment.com/)
1. [Pipefy](https://www.pipefy.com/)
1. [Pipekit](https://www.pipekit.io)
1. [Pismo](https://pismo.io/)
1. [Platform9 Systems](https://platform9.com/)
1. [Polarpoint.io](https://polarpoint.io)
Expand Down
11 changes: 6 additions & 5 deletions cmd/argocd/commands/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import (
"github.com/argoproj/argo-cd/v2/util/argo"
argodiff "github.com/argoproj/argo-cd/v2/util/argo/diff"
"github.com/argoproj/argo-cd/v2/util/cli"
"github.com/argoproj/argo-cd/v2/util/app/discovery"
"github.com/argoproj/argo-cd/v2/util/errors"
"github.com/argoproj/argo-cd/v2/util/git"
"github.com/argoproj/argo-cd/v2/util/grpc"
Expand Down Expand Up @@ -318,7 +319,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
var command = &cobra.Command{
Use: "get APPNAME",
Short: "Get application details",
Example: templates.Examples(`
Example: templates.Examples(`
# Get basic details about the application "my-app" in wide format
argocd app get my-app -o wide
Expand All @@ -342,7 +343,7 @@ func NewApplicationGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
# Get application details and display them in a tree format
argocd app get my-app --output tree
# Get application details and display them in a detailed tree format
argocd app get my-app --output tree=detailed
`),
Expand Down Expand Up @@ -431,7 +432,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
var command = &cobra.Command{
Use: "logs APPNAME",
Short: "Get logs of application pods",
Example: templates.Examples(`
Example: templates.Examples(`
# Get logs of pods associated with the application "my-app"
argocd app logs my-app
Expand Down Expand Up @@ -716,7 +717,7 @@ func NewApplicationSetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Com
var command = &cobra.Command{
Use: "set APPNAME",
Short: "Set application parameters",
Example: templates.Examples(`
Example: templates.Examples(`
# Set application parameters for the application "my-app"
argocd app set my-app --parameter key1=value1 --parameter key2=value2
Expand Down Expand Up @@ -992,7 +993,7 @@ func getLocalObjects(ctx context.Context, app *argoappv1.Application, proj *argo
func getLocalObjectsString(ctx context.Context, app *argoappv1.Application, proj *argoappv1.AppProject, local, localRepoRoot, appLabelKey, kubeVersion string, apiVersions []string, kustomizeOptions *argoappv1.KustomizeOptions,
trackingMethod string) []string {
source := app.Spec.GetSource()
res, err := repository.GenerateManifests(ctx, local, localRepoRoot, source.TargetRevision, &repoapiclient.ManifestRequest{
res, err := repository.GenerateManifests(ctx, discovery.NewNoServices(), local, localRepoRoot, source.TargetRevision, &repoapiclient.ManifestRequest{
Repo: &argoappv1.Repository{Repo: source.RepoURL},
AppLabelKey: appLabelKey,
AppName: app.Name,
Expand Down
48 changes: 41 additions & 7 deletions cmpserver/apiclient/clientset.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,53 @@ type Clientset interface {
NewConfigManagementPluginClient() (io.Closer, ConfigManagementPluginServiceClient, error)
}

type ClientType int

const (
Sidecar ClientType = iota
Service
)

func (ct *ClientType) addrType() string {
switch *ct {
case Sidecar:
return "unix"
case Service:
return "tcp"
default:
return ""
}
}

func (ct *ClientType) String() string {
switch *ct {
case Sidecar:
return "sidecar"
case Service:
return "service"
default:
return "unknown"
}
}

type clientSet struct {
address string
address string
clientType ClientType
}

func (c *clientSet) addrType() string {
return c.clientType.addrType()
}

func (c *clientSet) NewConfigManagementPluginClient() (io.Closer, ConfigManagementPluginServiceClient, error) {
conn, err := NewConnection(c.address)
conn, err := c.newConnection()
if err != nil {
return nil, nil, err
}
return conn, NewConfigManagementPluginServiceClient(conn), nil
}

func NewConnection(address string) (*grpc.ClientConn, error) {
func (c *clientSet) newConnection() (*grpc.ClientConn, error) {
retryOpts := []grpc_retry.CallOption{
grpc_retry.WithMax(3),
grpc_retry.WithBackoff(grpc_retry.BackoffLinear(1000 * time.Millisecond)),
Expand All @@ -51,15 +85,15 @@ func NewConnection(address string) (*grpc.ClientConn, error) {
}

dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
conn, err := grpc_util.BlockingDial(context.Background(), "unix", address, nil, dialOpts...)
conn, err := grpc_util.BlockingDial(context.Background(), c.addrType(), c.address, nil, dialOpts...)
if err != nil {
log.Errorf("Unable to connect to config management plugin service with address %s", address)
log.Errorf("Unable to connect to config management plugin with address %s (type %s)", c.address, c.clientType.String())
return nil, err
}
return conn, nil
}

// NewConfigManagementPluginClientSet creates new instance of config management plugin server Clientset
func NewConfigManagementPluginClientSet(address string) Clientset {
return &clientSet{address: address}
func NewConfigManagementPluginClientSet(address string, clientType ClientType) Clientset {
return &clientSet{address: address, clientType: clientType}
}
9 changes: 7 additions & 2 deletions cmpserver/plugin/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ type PluginConfig struct {

type PluginConfigSpec struct {
Version string `json:"version"`
ListenAddress string `json:"listenAddress,omitempty"`
Init Command `json:"init,omitempty"`
Generate Command `json:"generate"`
Discover Discover `json:"discover"`
Expand Down Expand Up @@ -93,13 +94,17 @@ func ValidatePluginConfig(config PluginConfig) error {
return nil
}

func (cfg *PluginConfig) Address() string {
// Returns the listen address and whether this is a tcp or unix address
func (cfg *PluginConfig) Address() (string, string) {
var address string
if cfg.Spec.ListenAddress != "" {
return cfg.Spec.ListenAddress, `tcp`
}
pluginSockFilePath := common.GetPluginSockFilePath()
if cfg.Spec.Version != "" {
address = fmt.Sprintf("%s/%s-%s.sock", pluginSockFilePath, cfg.Metadata.Name, cfg.Spec.Version)
} else {
address = fmt.Sprintf("%s/%s.sock", pluginSockFilePath, cfg.Metadata.Name)
}
return address
return address, `unix`
}
41 changes: 33 additions & 8 deletions cmpserver/plugin/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,10 @@ spec:

func Test_PluginConfig_Address(t *testing.T) {
testCases := []struct {
name string
config *PluginConfig
expected string
name string
config *PluginConfig
expected string
expectedType string
}{
{
name: "no version specified",
Expand All @@ -184,7 +185,8 @@ func Test_PluginConfig_Address(t *testing.T) {
Name: "name",
},
},
expected: "name",
expected: "name",
expectedType: "unix",
},
{
name: "version specified",
Expand All @@ -199,17 +201,40 @@ func Test_PluginConfig_Address(t *testing.T) {
Version: "version",
},
},
expected: "name-version",
expected: "name-version",
expectedType: "unix",
},
{
name: "version specified",
config: &PluginConfig{
TypeMeta: v1.TypeMeta{
Kind: ConfigManagementPluginKind,
},
Metadata: v1.ObjectMeta{
Name: "name",
},
Spec: PluginConfigSpec{
Version: "version",
ListenAddress: "0.0.0.0:8080",
},
},
expected: "0.0.0.0:8080",
expectedType: "tcp",
},
}

for _, tc := range testCases {
tcc := tc
t.Run(tcc.name, func(t *testing.T) {
t.Parallel()
actual := tcc.config.Address()
expectedAddress := fmt.Sprintf("%s/%s.sock", common.GetPluginSockFilePath(), tcc.expected)
assert.Equal(t, expectedAddress, actual)
actual, addrType := tcc.config.Address()
assert.Equal(t, tcc.expectedType, addrType)
if tcc.expectedType == `unix` {
expectedAddress := fmt.Sprintf("%s/%s.sock", common.GetPluginSockFilePath(), tcc.expected)
assert.Equal(t, expectedAddress, actual)
} else {
assert.Equal(t, tcc.expected, actual)
}
})
}
}
10 changes: 7 additions & 3 deletions cmpserver/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,17 @@ func (a *ArgoCDCMPServer) Run() {
config := a.initConstants.PluginConfig

// Listen on the socket address
_ = os.Remove(config.Address())
listener, err := net.Listen("unix", config.Address())
address, addressType := config.Address()
if addressType == `unix` {
_ = os.Remove(address)
}
listener, err := net.Listen(addressType, address)

errors.CheckError(err)
log.Infof("argocd-cmp-server %s serving on %s", common.GetVersion(), listener.Addr())

signal.Notify(a.stopCh, syscall.SIGINT, syscall.SIGTERM)
go a.Shutdown(config.Address())
go a.Shutdown(address)

grpcServer, err := a.CreateGRPC()
errors.CheckError(err)
Expand Down
Loading

0 comments on commit 782c4e7

Please sign in to comment.