mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 09:38:49 +01:00
Compare commits
14 Commits
hydrator-c
...
v1.1.0-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2756210d9 | ||
|
|
97565c0895 | ||
|
|
9a6f9ff824 | ||
|
|
290cefaedd | ||
|
|
69e49d708f | ||
|
|
e27be81947 | ||
|
|
4febc66a64 | ||
|
|
a984af76f1 | ||
|
|
be6a0fc21f | ||
|
|
122729e2a4 | ||
|
|
84635d4dbe | ||
|
|
46550e009b | ||
|
|
683b9072b8 | ||
|
|
33e66dcf5e |
2
Gopkg.lock
generated
2
Gopkg.lock
generated
@@ -1496,7 +1496,7 @@
|
||||
version = "v0.1.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:aae856b28533bfdb39112514290f7722d00a55a99d2d0b897c6ee82e6feb87b3"
|
||||
digest = "1:6fb17dac788a9f25fc454d8324180779687c46b60686c6e64136183c8134111e"
|
||||
name = "k8s.io/kube-openapi"
|
||||
packages = [
|
||||
"cmd/openapi-gen",
|
||||
|
||||
@@ -868,6 +868,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/clusters/{server}/rotate-auth": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"ClusterService"
|
||||
],
|
||||
"summary": "RotateAuth returns a cluster by server address",
|
||||
"operationId": "RotateAuth",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"name": "server",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "(empty)",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/clusterClusterResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/projects": {
|
||||
"get": {
|
||||
"tags": [
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
clusterpkg "github.com/argoproj/argo-cd/pkg/apiclient/cluster"
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/clusterauth"
|
||||
)
|
||||
|
||||
// NewClusterCommand returns a new instance of an `argocd cluster` command
|
||||
@@ -39,6 +40,7 @@ func NewClusterCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clientc
|
||||
command.AddCommand(NewClusterGetCommand(clientOpts))
|
||||
command.AddCommand(NewClusterListCommand(clientOpts))
|
||||
command.AddCommand(NewClusterRemoveCommand(clientOpts))
|
||||
command.AddCommand(NewClusterRotateAuthCommand(clientOpts))
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -86,7 +88,7 @@ func NewClusterAddCommand(clientOpts *argocdclient.ClientOptions, pathOpts *clie
|
||||
// Install RBAC resources for managing the cluster
|
||||
clientset, err := kubernetes.NewForConfig(conf)
|
||||
errors.CheckError(err)
|
||||
managerBearerToken, err = common.InstallClusterManagerRBAC(clientset, systemNamespace)
|
||||
managerBearerToken, err = clusterauth.InstallClusterManagerRBAC(clientset, systemNamespace)
|
||||
errors.CheckError(err)
|
||||
}
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
@@ -178,7 +180,7 @@ func NewCluster(name string, conf *rest.Config, managerBearerToken string, awsAu
|
||||
// NewClusterGetCommand returns a new instance of an `argocd cluster get` command
|
||||
func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "get",
|
||||
Use: "get CLUSTER",
|
||||
Short: "Get cluster information",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
@@ -202,7 +204,7 @@ func NewClusterGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
|
||||
// NewClusterRemoveCommand returns a new instance of an `argocd cluster list` command
|
||||
func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "rm",
|
||||
Use: "rm CLUSTER",
|
||||
Short: "Remove cluster credentials",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) == 0 {
|
||||
@@ -217,7 +219,7 @@ func NewClusterRemoveCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comm
|
||||
|
||||
for _, clusterName := range args {
|
||||
// TODO(jessesuen): find the right context and remove manager RBAC artifacts
|
||||
// err := common.UninstallClusterManagerRBAC(clientset)
|
||||
// err := clusterauth.UninstallClusterManagerRBAC(clientset)
|
||||
// errors.CheckError(err)
|
||||
_, err := clusterIf.Delete(context.Background(), &clusterpkg.ClusterQuery{Server: clusterName})
|
||||
errors.CheckError(err)
|
||||
@@ -247,3 +249,26 @@ func NewClusterListCommand(clientOpts *argocdclient.ClientOptions) *cobra.Comman
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
// NewClusterRotateAuthCommand returns a new instance of an `argocd cluster rotate-auth` command
|
||||
func NewClusterRotateAuthCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command {
|
||||
var command = &cobra.Command{
|
||||
Use: "rotate-auth CLUSTER",
|
||||
Short: fmt.Sprintf("%s cluster rotate-auth CLUSTER", cliName),
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
if len(args) != 1 {
|
||||
c.HelpFunc()(c, args)
|
||||
os.Exit(1)
|
||||
}
|
||||
conn, clusterIf := argocdclient.NewClientOrDie(clientOpts).NewClusterClientOrDie()
|
||||
defer util.Close(conn)
|
||||
clusterQuery := clusterpkg.ClusterQuery{
|
||||
Server: args[0],
|
||||
}
|
||||
_, err := clusterIf.RotateAuth(context.Background(), &clusterQuery)
|
||||
errors.CheckError(err)
|
||||
fmt.Printf("Cluster '%s' rotated auth\n", clusterQuery.Server)
|
||||
},
|
||||
}
|
||||
return command
|
||||
}
|
||||
|
||||
@@ -113,7 +113,10 @@ func NewApplicationController(
|
||||
appInformer, appLister := ctrl.newApplicationInformerAndLister()
|
||||
projInformer := v1alpha1.NewAppProjectInformer(applicationClientset, namespace, appResyncPeriod, cache.Indexers{})
|
||||
metricsAddr := fmt.Sprintf("0.0.0.0:%d", metricsPort)
|
||||
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister)
|
||||
ctrl.metricsServer = metrics.NewMetricsServer(metricsAddr, appLister, func() error {
|
||||
_, err := kubeClientset.Discovery().ServerVersion()
|
||||
return err
|
||||
})
|
||||
stateCache := statecache.NewLiveStateCache(db, appInformer, ctrl.settings, kubectlCmd, ctrl.metricsServer, ctrl.handleAppUpdated)
|
||||
appStateManager := NewAppStateManager(db, applicationClientset, repoClientset, namespace, kubectlCmd, ctrl.settings, stateCache, projInformer, ctrl.metricsServer)
|
||||
ctrl.appInformer = appInformer
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
applister "github.com/argoproj/argo-cd/pkg/client/listers/application/v1alpha1"
|
||||
"github.com/argoproj/argo-cd/util/git"
|
||||
"github.com/argoproj/argo-cd/util/healthz"
|
||||
)
|
||||
|
||||
type MetricsServer struct {
|
||||
@@ -59,12 +60,13 @@ var (
|
||||
)
|
||||
|
||||
// NewMetricsServer returns a new prometheus server which collects application metrics
|
||||
func NewMetricsServer(addr string, appLister applister.ApplicationLister) *MetricsServer {
|
||||
func NewMetricsServer(addr string, appLister applister.ApplicationLister, healthCheck func() error) *MetricsServer {
|
||||
mux := http.NewServeMux()
|
||||
appRegistry := NewAppRegistry(appLister)
|
||||
appRegistry.MustRegister(prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}))
|
||||
appRegistry.MustRegister(prometheus.NewGoCollector())
|
||||
mux.Handle(MetricsPath, promhttp.HandlerFor(appRegistry, promhttp.HandlerOpts{}))
|
||||
healthz.ServeHealthCheck(mux, healthCheck)
|
||||
|
||||
syncCounter := prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
|
||||
@@ -104,6 +104,10 @@ argocd_app_sync_status{name="my-app",namespace="argocd",project="default",sync_s
|
||||
argocd_app_sync_status{name="my-app",namespace="argocd",project="default",sync_status="Unknown"} 0
|
||||
`
|
||||
|
||||
var noOpHealthCheck = func() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFakeApp(fakeApp string) *argoappv1.Application {
|
||||
var app argoappv1.Application
|
||||
err := yaml.Unmarshal([]byte(fakeApp), &app)
|
||||
@@ -133,7 +137,7 @@ func newFakeLister(fakeApp ...string) (context.CancelFunc, applister.Application
|
||||
func testApp(t *testing.T, fakeApp string, expectedResponse string) {
|
||||
cancel, appLister := newFakeLister(fakeApp)
|
||||
defer cancel()
|
||||
metricsServ := NewMetricsServer("localhost:8082", appLister)
|
||||
metricsServ := NewMetricsServer("localhost:8082", appLister, noOpHealthCheck)
|
||||
req, err := http.NewRequest("GET", "/metrics", nil)
|
||||
assert.NoError(t, err)
|
||||
rr := httptest.NewRecorder()
|
||||
@@ -176,7 +180,7 @@ argocd_app_sync_total{name="my-app",namespace="argocd",phase="Succeeded",project
|
||||
func TestMetricsSyncCounter(t *testing.T) {
|
||||
cancel, appLister := newFakeLister()
|
||||
defer cancel()
|
||||
metricsServ := NewMetricsServer("localhost:8082", appLister)
|
||||
metricsServ := NewMetricsServer("localhost:8082", appLister, noOpHealthCheck)
|
||||
|
||||
fakeApp := newFakeApp(fakeApp)
|
||||
metricsServ.IncSync(fakeApp, &argoappv1.OperationState{Phase: argoappv1.OperationRunning})
|
||||
@@ -217,7 +221,7 @@ argocd_app_reconcile_count{name="my-app",namespace="argocd",project="important-p
|
||||
func TestReconcileMetrics(t *testing.T) {
|
||||
cancel, appLister := newFakeLister()
|
||||
defer cancel()
|
||||
metricsServ := NewMetricsServer("localhost:8082", appLister)
|
||||
metricsServ := NewMetricsServer("localhost:8082", appLister, noOpHealthCheck)
|
||||
|
||||
fakeApp := newFakeApp(fakeApp)
|
||||
metricsServ.IncReconcile(fakeApp, 5*time.Second)
|
||||
|
||||
@@ -457,7 +457,8 @@ func (sc *syncContext) liveObj(obj *unstructured.Unstructured) *unstructured.Uns
|
||||
for _, resource := range sc.compareResult.managedResources {
|
||||
if resource.Group == obj.GroupVersionKind().Group &&
|
||||
resource.Kind == obj.GetKind() &&
|
||||
resource.Namespace == obj.GetNamespace() &&
|
||||
// cluster scoped objects will not have a namespace, even if the user has defined it
|
||||
(resource.Namespace == "" || resource.Namespace == obj.GetNamespace()) &&
|
||||
resource.Name == obj.GetName() {
|
||||
return resource.Live
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -526,3 +527,37 @@ func Test_syncContext_isSelectiveSync(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_syncContext_liveObj(t *testing.T) {
|
||||
type fields struct {
|
||||
compareResult *comparisonResult
|
||||
}
|
||||
type args struct {
|
||||
obj *unstructured.Unstructured
|
||||
}
|
||||
obj := test.NewPod()
|
||||
obj.SetNamespace("my-ns")
|
||||
|
||||
found := test.NewPod()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *unstructured.Unstructured
|
||||
}{
|
||||
{"None", fields{compareResult: &comparisonResult{managedResources: []managedResource{}}}, args{obj: &unstructured.Unstructured{}}, nil},
|
||||
{"Found", fields{compareResult: &comparisonResult{managedResources: []managedResource{{Group: obj.GroupVersionKind().Group, Kind: obj.GetKind(), Namespace: obj.GetNamespace(), Name: obj.GetName(), Live: found}}}}, args{obj: obj}, found},
|
||||
{"EmptyNamespace", fields{compareResult: &comparisonResult{managedResources: []managedResource{{Group: obj.GroupVersionKind().Group, Kind: obj.GetKind(), Name: obj.GetName(), Live: found}}}}, args{obj: obj}, found},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
sc := &syncContext{
|
||||
compareResult: tt.fields.compareResult,
|
||||
}
|
||||
if got := sc.liveObj(tt.args.obj); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("syncContext.liveObj() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,14 @@ spec:
|
||||
ports:
|
||||
- containerPort: 8082
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
|
||||
@@ -12,7 +12,7 @@ bases:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.1.0-rc2
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: latest
|
||||
newTag: v1.1.0-rc2
|
||||
|
||||
@@ -689,7 +689,6 @@ spec:
|
||||
- revision
|
||||
- deployedAt
|
||||
- id
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
observedAt: {}
|
||||
@@ -1164,7 +1163,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- revision
|
||||
- source
|
||||
type: object
|
||||
required:
|
||||
- operation
|
||||
|
||||
@@ -17,7 +17,7 @@ patchesStrategicMerge:
|
||||
images:
|
||||
- name: argoproj/argocd
|
||||
newName: argoproj/argocd
|
||||
newTag: latest
|
||||
newTag: v1.1.0-rc2
|
||||
- name: argoproj/argocd-ui
|
||||
newName: argoproj/argocd-ui
|
||||
newTag: latest
|
||||
newTag: v1.1.0-rc2
|
||||
|
||||
@@ -690,7 +690,6 @@ spec:
|
||||
- revision
|
||||
- deployedAt
|
||||
- id
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
observedAt: {}
|
||||
@@ -1165,7 +1164,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- revision
|
||||
- source
|
||||
type: object
|
||||
required:
|
||||
- operation
|
||||
@@ -2197,16 +2195,23 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
- containerPort: 8082
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8082
|
||||
serviceAccountName: argocd-application-controller
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -2244,7 +2249,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2299,7 +2304,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2361,7 +2366,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2388,7 +2393,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -690,7 +690,6 @@ spec:
|
||||
- revision
|
||||
- deployedAt
|
||||
- id
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
observedAt: {}
|
||||
@@ -1165,7 +1164,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- revision
|
||||
- source
|
||||
type: object
|
||||
required:
|
||||
- operation
|
||||
@@ -2112,16 +2110,23 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
- containerPort: 8082
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8082
|
||||
serviceAccountName: argocd-application-controller
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -2159,7 +2164,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2214,7 +2219,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2276,7 +2281,7 @@ spec:
|
||||
- argocd-redis-ha-announce-2:26379
|
||||
- --sentinelmaster
|
||||
- argocd
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2303,7 +2308,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -690,7 +690,6 @@ spec:
|
||||
- revision
|
||||
- deployedAt
|
||||
- id
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
observedAt: {}
|
||||
@@ -1165,7 +1164,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- revision
|
||||
- source
|
||||
type: object
|
||||
required:
|
||||
- operation
|
||||
@@ -1961,16 +1959,23 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
- containerPort: 8082
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8082
|
||||
serviceAccountName: argocd-application-controller
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -2008,7 +2013,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -2071,7 +2076,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2110,7 +2115,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2137,7 +2142,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -690,7 +690,6 @@ spec:
|
||||
- revision
|
||||
- deployedAt
|
||||
- id
|
||||
- source
|
||||
type: object
|
||||
type: array
|
||||
observedAt: {}
|
||||
@@ -1165,7 +1164,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- revision
|
||||
- source
|
||||
type: object
|
||||
required:
|
||||
- operation
|
||||
@@ -1876,16 +1874,23 @@ spec:
|
||||
- "20"
|
||||
- --operation-processors
|
||||
- "10"
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
name: argocd-application-controller
|
||||
ports:
|
||||
- containerPort: 8082
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: 8082
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
tcpSocket:
|
||||
port: 8082
|
||||
serviceAccountName: argocd-application-controller
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
@@ -1923,7 +1928,7 @@ spec:
|
||||
- cp
|
||||
- /usr/local/bin/argocd-util
|
||||
- /shared
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: copyutil
|
||||
volumeMounts:
|
||||
@@ -1986,7 +1991,7 @@ spec:
|
||||
- argocd-repo-server
|
||||
- --redis
|
||||
- argocd-redis:6379
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
initialDelaySeconds: 5
|
||||
@@ -2025,7 +2030,7 @@ spec:
|
||||
- argocd-server
|
||||
- --staticassets
|
||||
- /shared/app
|
||||
image: argoproj/argocd:latest
|
||||
image: argoproj/argocd:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
@@ -2052,7 +2057,7 @@ spec:
|
||||
- -r
|
||||
- /app
|
||||
- /shared
|
||||
image: argoproj/argocd-ui:latest
|
||||
image: argoproj/argocd-ui:v1.1.0-rc2
|
||||
imagePullPolicy: Always
|
||||
name: ui
|
||||
volumeMounts:
|
||||
|
||||
@@ -45,7 +45,7 @@ func (m *ClusterQuery) Reset() { *m = ClusterQuery{} }
|
||||
func (m *ClusterQuery) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterQuery) ProtoMessage() {}
|
||||
func (*ClusterQuery) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_cluster_63d48da351ddefcc, []int{0}
|
||||
return fileDescriptor_cluster_66eaae81439ad97b, []int{0}
|
||||
}
|
||||
func (m *ClusterQuery) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -91,7 +91,7 @@ func (m *ClusterResponse) Reset() { *m = ClusterResponse{} }
|
||||
func (m *ClusterResponse) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterResponse) ProtoMessage() {}
|
||||
func (*ClusterResponse) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_cluster_63d48da351ddefcc, []int{1}
|
||||
return fileDescriptor_cluster_66eaae81439ad97b, []int{1}
|
||||
}
|
||||
func (m *ClusterResponse) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -132,7 +132,7 @@ func (m *ClusterCreateRequest) Reset() { *m = ClusterCreateRequest{} }
|
||||
func (m *ClusterCreateRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterCreateRequest) ProtoMessage() {}
|
||||
func (*ClusterCreateRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_cluster_63d48da351ddefcc, []int{2}
|
||||
return fileDescriptor_cluster_66eaae81439ad97b, []int{2}
|
||||
}
|
||||
func (m *ClusterCreateRequest) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -186,7 +186,7 @@ func (m *ClusterUpdateRequest) Reset() { *m = ClusterUpdateRequest{} }
|
||||
func (m *ClusterUpdateRequest) String() string { return proto.CompactTextString(m) }
|
||||
func (*ClusterUpdateRequest) ProtoMessage() {}
|
||||
func (*ClusterUpdateRequest) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_cluster_63d48da351ddefcc, []int{3}
|
||||
return fileDescriptor_cluster_66eaae81439ad97b, []int{3}
|
||||
}
|
||||
func (m *ClusterUpdateRequest) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
@@ -250,6 +250,8 @@ type ClusterServiceClient interface {
|
||||
Update(ctx context.Context, in *ClusterUpdateRequest, opts ...grpc.CallOption) (*v1alpha1.Cluster, error)
|
||||
// Delete deletes a cluster
|
||||
Delete(ctx context.Context, in *ClusterQuery, opts ...grpc.CallOption) (*ClusterResponse, error)
|
||||
// RotateAuth returns a cluster by server address
|
||||
RotateAuth(ctx context.Context, in *ClusterQuery, opts ...grpc.CallOption) (*ClusterResponse, error)
|
||||
}
|
||||
|
||||
type clusterServiceClient struct {
|
||||
@@ -305,6 +307,15 @@ func (c *clusterServiceClient) Delete(ctx context.Context, in *ClusterQuery, opt
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *clusterServiceClient) RotateAuth(ctx context.Context, in *ClusterQuery, opts ...grpc.CallOption) (*ClusterResponse, error) {
|
||||
out := new(ClusterResponse)
|
||||
err := c.cc.Invoke(ctx, "/cluster.ClusterService/RotateAuth", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for ClusterService service
|
||||
|
||||
type ClusterServiceServer interface {
|
||||
@@ -318,6 +329,8 @@ type ClusterServiceServer interface {
|
||||
Update(context.Context, *ClusterUpdateRequest) (*v1alpha1.Cluster, error)
|
||||
// Delete deletes a cluster
|
||||
Delete(context.Context, *ClusterQuery) (*ClusterResponse, error)
|
||||
// RotateAuth returns a cluster by server address
|
||||
RotateAuth(context.Context, *ClusterQuery) (*ClusterResponse, error)
|
||||
}
|
||||
|
||||
func RegisterClusterServiceServer(s *grpc.Server, srv ClusterServiceServer) {
|
||||
@@ -414,6 +427,24 @@ func _ClusterService_Delete_Handler(srv interface{}, ctx context.Context, dec fu
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _ClusterService_RotateAuth_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ClusterQuery)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(ClusterServiceServer).RotateAuth(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/cluster.ClusterService/RotateAuth",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(ClusterServiceServer).RotateAuth(ctx, req.(*ClusterQuery))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _ClusterService_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "cluster.ClusterService",
|
||||
HandlerType: (*ClusterServiceServer)(nil),
|
||||
@@ -438,6 +469,10 @@ var _ClusterService_serviceDesc = grpc.ServiceDesc{
|
||||
MethodName: "Delete",
|
||||
Handler: _ClusterService_Delete_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "RotateAuth",
|
||||
Handler: _ClusterService_RotateAuth_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "server/cluster/cluster.proto",
|
||||
@@ -1061,39 +1096,41 @@ var (
|
||||
)
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("server/cluster/cluster.proto", fileDescriptor_cluster_63d48da351ddefcc)
|
||||
proto.RegisterFile("server/cluster/cluster.proto", fileDescriptor_cluster_66eaae81439ad97b)
|
||||
}
|
||||
|
||||
var fileDescriptor_cluster_63d48da351ddefcc = []byte{
|
||||
// 475 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x94, 0x4f, 0x8b, 0x13, 0x31,
|
||||
0x18, 0xc6, 0xc9, 0xaa, 0xa3, 0x46, 0xf1, 0x4f, 0x58, 0xa5, 0x8e, 0x6b, 0xd9, 0x9d, 0x83, 0x2c,
|
||||
0xa2, 0x09, 0xad, 0x17, 0xf1, 0x22, 0xec, 0x8a, 0x22, 0x78, 0xb1, 0xe2, 0x45, 0x16, 0x24, 0x3b,
|
||||
0x7d, 0xc9, 0xc6, 0x8e, 0x93, 0x98, 0x64, 0x06, 0x44, 0x44, 0xd0, 0xab, 0x78, 0xf1, 0x03, 0x78,
|
||||
0xf5, 0xa3, 0x78, 0x14, 0xfc, 0x02, 0x52, 0xfc, 0x20, 0x32, 0x99, 0xa4, 0xdd, 0xed, 0x50, 0x10,
|
||||
0x2c, 0x9e, 0x9a, 0xbc, 0x49, 0x9f, 0xf7, 0x97, 0x27, 0xcf, 0x04, 0x6f, 0x58, 0x30, 0x35, 0x18,
|
||||
0x96, 0x17, 0x95, 0x75, 0xf3, 0x5f, 0xaa, 0x8d, 0x72, 0x8a, 0x9c, 0x0c, 0xd3, 0x74, 0x5d, 0x28,
|
||||
0xa1, 0x7c, 0x8d, 0x35, 0xa3, 0x76, 0x39, 0xdd, 0x10, 0x4a, 0x89, 0x02, 0x18, 0xd7, 0x92, 0xf1,
|
||||
0xb2, 0x54, 0x8e, 0x3b, 0xa9, 0x4a, 0x1b, 0x56, 0xb3, 0xc9, 0x1d, 0x4b, 0xa5, 0xf2, 0xab, 0xb9,
|
||||
0x32, 0xc0, 0xea, 0x01, 0x13, 0x50, 0x82, 0xe1, 0x0e, 0xc6, 0x61, 0xcf, 0x23, 0x21, 0xdd, 0x41,
|
||||
0xb5, 0x4f, 0x73, 0xf5, 0x8a, 0x71, 0xe3, 0x5b, 0xbc, 0xf4, 0x83, 0x5b, 0xf9, 0x98, 0xe9, 0x89,
|
||||
0x68, 0xfe, 0x6c, 0x19, 0xd7, 0xba, 0x90, 0xb9, 0x17, 0x67, 0xf5, 0x80, 0x17, 0xfa, 0x80, 0x77,
|
||||
0xa4, 0xb2, 0xeb, 0xf8, 0xec, 0x6e, 0x4b, 0xfb, 0xa4, 0x02, 0xf3, 0x86, 0x5c, 0xc6, 0x49, 0x7b,
|
||||
0xb6, 0x1e, 0xda, 0x44, 0xdb, 0xa7, 0x47, 0x61, 0x96, 0x5d, 0xc4, 0xe7, 0xc3, 0xbe, 0x11, 0x58,
|
||||
0xad, 0x4a, 0x0b, 0xd9, 0x27, 0x84, 0xd7, 0x43, 0x6d, 0xd7, 0x00, 0x77, 0x30, 0x82, 0xd7, 0x15,
|
||||
0x58, 0x47, 0xf6, 0x70, 0x74, 0xc0, 0x8b, 0x9c, 0x19, 0xee, 0xd0, 0x39, 0x30, 0x8d, 0xc0, 0x7e,
|
||||
0xf0, 0x22, 0x1f, 0x53, 0x3d, 0x11, 0xb4, 0x01, 0xa6, 0x87, 0x80, 0x69, 0x04, 0xa6, 0xb1, 0x6b,
|
||||
0x94, 0x6c, 0x08, 0x2b, 0x6d, 0xc1, 0xb8, 0xde, 0xda, 0x26, 0xda, 0x3e, 0x35, 0x0a, 0xb3, 0xcc,
|
||||
0xcd, 0x68, 0x9e, 0xe9, 0xf1, 0xff, 0xa2, 0x19, 0x7e, 0x3b, 0x81, 0xcf, 0x85, 0xe2, 0x53, 0x30,
|
||||
0xb5, 0xcc, 0x81, 0xbc, 0xc7, 0xc7, 0x1f, 0x4b, 0xeb, 0xc8, 0x25, 0x1a, 0x63, 0x71, 0xd8, 0xe1,
|
||||
0xf4, 0xc1, 0xbf, 0xb7, 0x6f, 0xe4, 0xb3, 0xde, 0x87, 0x9f, 0xbf, 0xbf, 0xac, 0x11, 0x72, 0xc1,
|
||||
0x47, 0xa5, 0x1e, 0xc4, 0x10, 0x5a, 0xf2, 0x19, 0xe1, 0xa4, 0xbd, 0x11, 0x72, 0x6d, 0x91, 0xe1,
|
||||
0xc8, 0x4d, 0xa5, 0x2b, 0xb0, 0x22, 0xdb, 0xf2, 0x1c, 0x57, 0xb3, 0x0e, 0xc7, 0xdd, 0xd9, 0x95,
|
||||
0x7d, 0x44, 0xf8, 0xd8, 0x43, 0x58, 0xea, 0xc8, 0x0a, 0x29, 0xc8, 0x95, 0x45, 0x0a, 0xf6, 0xb6,
|
||||
0x4d, 0xf0, 0x3b, 0xf2, 0x15, 0xe1, 0xa4, 0x8d, 0x46, 0xd7, 0x96, 0x23, 0x91, 0x59, 0x09, 0xd0,
|
||||
0xd0, 0x03, 0xdd, 0x4c, 0xb7, 0xba, 0x40, 0xb1, 0x77, 0x00, 0x9b, 0xfb, 0xb4, 0x87, 0x93, 0xfb,
|
||||
0x50, 0x80, 0x83, 0x65, 0x4e, 0xf5, 0x16, 0xcb, 0xb3, 0x8f, 0x31, 0x9c, 0xff, 0xc6, 0xf2, 0xf3,
|
||||
0xef, 0xdc, 0xfb, 0x3e, 0xed, 0xa3, 0x1f, 0xd3, 0x3e, 0xfa, 0x35, 0xed, 0xa3, 0xe7, 0x83, 0xbf,
|
||||
0x78, 0x43, 0xf2, 0x42, 0x42, 0xe9, 0xa2, 0xd4, 0x7e, 0xe2, 0x9f, 0x8c, 0xdb, 0x7f, 0x02, 0x00,
|
||||
0x00, 0xff, 0xff, 0x9f, 0x92, 0x8b, 0xe2, 0xfe, 0x04, 0x00, 0x00,
|
||||
var fileDescriptor_cluster_66eaae81439ad97b = []byte{
|
||||
// 502 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x94, 0x4f, 0x8b, 0x13, 0x3f,
|
||||
0x18, 0xc7, 0xc9, 0xfe, 0x7e, 0x8c, 0x1a, 0xc5, 0x3f, 0x61, 0x95, 0x3a, 0xae, 0x65, 0x37, 0xa0,
|
||||
0x2e, 0x62, 0x13, 0x5a, 0x2f, 0xe2, 0x45, 0xdc, 0x15, 0x45, 0xf0, 0xe2, 0x88, 0x17, 0x59, 0x90,
|
||||
0xec, 0xf4, 0x61, 0x3a, 0x76, 0x9c, 0xc4, 0x24, 0x33, 0x20, 0x22, 0x82, 0x5e, 0xc5, 0x8b, 0xe0,
|
||||
0xd5, 0xb7, 0xe3, 0x51, 0xf0, 0x0d, 0x48, 0xf1, 0x85, 0xc8, 0x64, 0x92, 0x76, 0xb7, 0xa5, 0xa2,
|
||||
0x58, 0x3c, 0x35, 0x79, 0x92, 0x7e, 0xbf, 0x9f, 0x7c, 0xf3, 0x4c, 0xf0, 0x86, 0x01, 0x5d, 0x83,
|
||||
0xe6, 0x69, 0x51, 0x19, 0x3b, 0xfb, 0x65, 0x4a, 0x4b, 0x2b, 0xc9, 0x11, 0x3f, 0x8d, 0xd7, 0x33,
|
||||
0x99, 0x49, 0x57, 0xe3, 0xcd, 0xa8, 0x5d, 0x8e, 0x37, 0x32, 0x29, 0xb3, 0x02, 0xb8, 0x50, 0x39,
|
||||
0x17, 0x65, 0x29, 0xad, 0xb0, 0xb9, 0x2c, 0x8d, 0x5f, 0xa5, 0xe3, 0x1b, 0x86, 0xe5, 0xd2, 0xad,
|
||||
0xa6, 0x52, 0x03, 0xaf, 0xfb, 0x3c, 0x83, 0x12, 0xb4, 0xb0, 0x30, 0xf4, 0x7b, 0xee, 0x67, 0xb9,
|
||||
0x1d, 0x55, 0xfb, 0x2c, 0x95, 0xcf, 0xb9, 0xd0, 0xce, 0xe2, 0x99, 0x1b, 0xf4, 0xd2, 0x21, 0x57,
|
||||
0xe3, 0xac, 0xf9, 0xb3, 0xe1, 0x42, 0xa9, 0x22, 0x4f, 0x9d, 0x38, 0xaf, 0xfb, 0xa2, 0x50, 0x23,
|
||||
0xb1, 0x20, 0x45, 0x2f, 0xe3, 0x13, 0xbb, 0x2d, 0xed, 0xc3, 0x0a, 0xf4, 0x4b, 0x72, 0x0e, 0x47,
|
||||
0xed, 0xd9, 0x3a, 0x68, 0x13, 0x6d, 0x1f, 0x4b, 0xfc, 0x8c, 0x9e, 0xc1, 0xa7, 0xfc, 0xbe, 0x04,
|
||||
0x8c, 0x92, 0xa5, 0x01, 0xfa, 0x1e, 0xe1, 0x75, 0x5f, 0xdb, 0xd5, 0x20, 0x2c, 0x24, 0xf0, 0xa2,
|
||||
0x02, 0x63, 0xc9, 0x1e, 0x0e, 0x09, 0x38, 0x91, 0xe3, 0x83, 0x1d, 0x36, 0x03, 0x66, 0x01, 0xd8,
|
||||
0x0d, 0x9e, 0xa6, 0x43, 0xa6, 0xc6, 0x19, 0x6b, 0x80, 0xd9, 0x01, 0x60, 0x16, 0x80, 0x59, 0x70,
|
||||
0x0d, 0x92, 0x0d, 0x61, 0xa5, 0x0c, 0x68, 0xdb, 0x59, 0xdb, 0x44, 0xdb, 0x47, 0x13, 0x3f, 0xa3,
|
||||
0x76, 0x4a, 0xf3, 0x58, 0x0d, 0xff, 0x15, 0xcd, 0xe0, 0x53, 0x84, 0x4f, 0xfa, 0xe2, 0x23, 0xd0,
|
||||
0x75, 0x9e, 0x02, 0x79, 0x83, 0xff, 0x7f, 0x90, 0x1b, 0x4b, 0xce, 0xb2, 0xd0, 0x16, 0x07, 0x13,
|
||||
0x8e, 0xef, 0xfe, 0xbd, 0x7d, 0x23, 0x4f, 0x3b, 0x6f, 0xbf, 0xfd, 0xf8, 0xb8, 0x46, 0xc8, 0x69,
|
||||
0xd7, 0x2a, 0x75, 0x3f, 0x34, 0xa1, 0x21, 0x1f, 0x10, 0x8e, 0xda, 0x1b, 0x21, 0x17, 0xe7, 0x19,
|
||||
0x0e, 0xdd, 0x54, 0xbc, 0x82, 0x28, 0xe8, 0x96, 0xe3, 0xb8, 0x40, 0x17, 0x38, 0x6e, 0x4e, 0xaf,
|
||||
0xec, 0x1d, 0xc2, 0xff, 0xdd, 0x83, 0xa5, 0x89, 0xac, 0x90, 0x82, 0x9c, 0x9f, 0xa7, 0xe0, 0xaf,
|
||||
0xda, 0x0e, 0x7e, 0x4d, 0x3e, 0x23, 0x1c, 0xb5, 0xad, 0xb1, 0x18, 0xcb, 0xa1, 0x96, 0x59, 0x09,
|
||||
0xd0, 0xc0, 0x01, 0x5d, 0x8b, 0xb7, 0x16, 0x81, 0x82, 0xb7, 0x07, 0x9b, 0xe5, 0xb4, 0x87, 0xa3,
|
||||
0x3b, 0x50, 0x80, 0x85, 0x65, 0x49, 0x75, 0xe6, 0xcb, 0xd3, 0x8f, 0xd1, 0x9f, 0xff, 0xea, 0x2f,
|
||||
0xce, 0x5f, 0x60, 0x9c, 0x34, 0x8f, 0x0d, 0xdc, 0xae, 0xec, 0xe8, 0xcf, 0x1d, 0x7a, 0xce, 0xe1,
|
||||
0x0a, 0xbd, 0xb4, 0xd4, 0x81, 0x6b, 0x27, 0xdf, 0x13, 0x95, 0x1d, 0xed, 0xdc, 0xfa, 0x32, 0xe9,
|
||||
0xa2, 0xaf, 0x93, 0x2e, 0xfa, 0x3e, 0xe9, 0xa2, 0x27, 0xfd, 0xdf, 0x78, 0xb1, 0xd2, 0x22, 0x87,
|
||||
0xd2, 0x06, 0xd9, 0xfd, 0xc8, 0x3d, 0x50, 0xd7, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff, 0x4e, 0xde,
|
||||
0x52, 0x19, 0x6c, 0x05, 0x00, 0x00,
|
||||
}
|
||||
|
||||
@@ -151,6 +151,33 @@ func request_ClusterService_Delete_0(ctx context.Context, marshaler runtime.Mars
|
||||
|
||||
}
|
||||
|
||||
func request_ClusterService_RotateAuth_0(ctx context.Context, marshaler runtime.Marshaler, client ClusterServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
|
||||
var protoReq ClusterQuery
|
||||
var metadata runtime.ServerMetadata
|
||||
|
||||
var (
|
||||
val string
|
||||
ok bool
|
||||
err error
|
||||
_ = err
|
||||
)
|
||||
|
||||
val, ok = pathParams["server"]
|
||||
if !ok {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "server")
|
||||
}
|
||||
|
||||
protoReq.Server, err = runtime.String(val)
|
||||
|
||||
if err != nil {
|
||||
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "server", err)
|
||||
}
|
||||
|
||||
msg, err := client.RotateAuth(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
|
||||
return msg, metadata, err
|
||||
|
||||
}
|
||||
|
||||
// RegisterClusterServiceHandlerFromEndpoint is same as RegisterClusterServiceHandler but
|
||||
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
|
||||
func RegisterClusterServiceHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
|
||||
@@ -334,6 +361,35 @@ func RegisterClusterServiceHandlerClient(ctx context.Context, mux *runtime.Serve
|
||||
|
||||
})
|
||||
|
||||
mux.Handle("POST", pattern_ClusterService_RotateAuth_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
|
||||
ctx, cancel := context.WithCancel(req.Context())
|
||||
defer cancel()
|
||||
if cn, ok := w.(http.CloseNotifier); ok {
|
||||
go func(done <-chan struct{}, closed <-chan bool) {
|
||||
select {
|
||||
case <-done:
|
||||
case <-closed:
|
||||
cancel()
|
||||
}
|
||||
}(ctx.Done(), cn.CloseNotify())
|
||||
}
|
||||
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
|
||||
rctx, err := runtime.AnnotateContext(ctx, mux, req)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
resp, md, err := request_ClusterService_RotateAuth_0(rctx, inboundMarshaler, client, req, pathParams)
|
||||
ctx = runtime.NewServerMetadataContext(ctx, md)
|
||||
if err != nil {
|
||||
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
forward_ClusterService_RotateAuth_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
|
||||
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -347,6 +403,8 @@ var (
|
||||
pattern_ClusterService_Update_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "clusters", "cluster.server"}, ""))
|
||||
|
||||
pattern_ClusterService_Delete_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "clusters", "server"}, ""))
|
||||
|
||||
pattern_ClusterService_RotateAuth_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "clusters", "server", "rotate-auth"}, ""))
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -359,4 +417,6 @@ var (
|
||||
forward_ClusterService_Update_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ClusterService_Delete_0 = runtime.ForwardResponseMessage
|
||||
|
||||
forward_ClusterService_RotateAuth_0 = runtime.ForwardResponseMessage
|
||||
)
|
||||
|
||||
@@ -326,7 +326,7 @@ func schema_pkg_apis_application_v1alpha1_Application(ref common.ReferenceCallba
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"metadata", "spec", "status"},
|
||||
Required: []string{"metadata", "spec"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
@@ -2442,7 +2442,7 @@ func schema_pkg_apis_application_v1alpha1_RevisionHistory(ref common.ReferenceCa
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"revision", "deployedAt", "id", "source"},
|
||||
Required: []string{"revision", "deployedAt", "id"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
@@ -2577,7 +2577,7 @@ func schema_pkg_apis_application_v1alpha1_SyncOperationResult(ref common.Referen
|
||||
},
|
||||
},
|
||||
},
|
||||
Required: []string{"revision", "source"},
|
||||
Required: []string{"revision"},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
|
||||
@@ -30,7 +30,7 @@ type Application struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata" protobuf:"bytes,1,opt,name=metadata"`
|
||||
Spec ApplicationSpec `json:"spec" protobuf:"bytes,2,opt,name=spec"`
|
||||
Status ApplicationStatus `json:"status" protobuf:"bytes,3,opt,name=status"`
|
||||
Status ApplicationStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
|
||||
Operation *Operation `json:"operation,omitempty" protobuf:"bytes,4,opt,name=operation"`
|
||||
}
|
||||
|
||||
@@ -394,7 +394,7 @@ type SyncOperationResult struct {
|
||||
// Revision holds the git commit SHA of the sync
|
||||
Revision string `json:"revision" protobuf:"bytes,2,opt,name=revision"`
|
||||
// Source records the application source information of the sync, used for comparing auto-sync
|
||||
Source ApplicationSource `json:"source" protobuf:"bytes,3,opt,name=source"`
|
||||
Source ApplicationSource `json:"source,omitempty" protobuf:"bytes,3,opt,name=source"`
|
||||
}
|
||||
|
||||
type ResultCode string
|
||||
@@ -476,7 +476,7 @@ type RevisionHistory struct {
|
||||
Revision string `json:"revision" protobuf:"bytes,2,opt,name=revision"`
|
||||
DeployedAt metav1.Time `json:"deployedAt" protobuf:"bytes,4,opt,name=deployedAt"`
|
||||
ID int64 `json:"id" protobuf:"bytes,5,opt,name=id"`
|
||||
Source ApplicationSource `json:"source" protobuf:"bytes,6,opt,name=source"`
|
||||
Source ApplicationSource `json:"source,omitempty" protobuf:"bytes,6,opt,name=source"`
|
||||
}
|
||||
|
||||
// ApplicationWatchEvent contains information about application change.
|
||||
|
||||
@@ -99,7 +99,7 @@ if obj.status ~= nil then
|
||||
end
|
||||
if obj.spec.strategy.canary ~= nil then
|
||||
currentRSIsStable = obj.status.canary.stableRS == obj.status.currentPodHash
|
||||
if obj.spec.strategy.canary.steps ~= nil then
|
||||
if obj.spec.strategy.canary.steps ~= nil and table.getn(obj.spec.strategy.canary.steps) > 0 then
|
||||
stepCount = table.getn(obj.spec.strategy.canary.steps)
|
||||
if obj.status.currentStepIndex ~= nil then
|
||||
currentStepIndex = obj.status.currentStepIndex
|
||||
|
||||
@@ -56,4 +56,8 @@ tests:
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: The rollout has completed canary deployment
|
||||
inputPath: testdata/canary/healthy_noSteps.yaml
|
||||
inputPath: testdata/canary/healthy_noSteps.yaml
|
||||
- healthStatus:
|
||||
status: Healthy
|
||||
message: The rollout has completed canary deployment
|
||||
inputPath: testdata/canary/healthy_emptyStepsList.yaml
|
||||
67
resource_customizations/argoproj.io/Rollout/testdata/canary/healthy_emptyStepsList.yaml
vendored
Normal file
67
resource_customizations/argoproj.io/Rollout/testdata/canary/healthy_emptyStepsList.yaml
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
annotations:
|
||||
kubectl.kubernetes.io/last-applied-configuration: >
|
||||
{"apiVersion":"argoproj.io/v1alpha1","kind":"Rollout","metadata":{"annotations":{},"labels":{"app.kubernetes.io/instance":"guestbook-canary","ksonnet.io/component":"guestbook-ui"},"name":"guestbook-canary","namespace":"default"},"spec":{"minReadySeconds":10,"replicas":5,"selector":{"matchLabels":{"app":"guestbook-canary"}},"strategy":{"canary":{"maxSurge":1,"maxUnavailable":0,"steps":[{"setWeight":20},{"pause":{"duration":30}},{"setWeight":40},{"pause":{}}]}},"template":{"metadata":{"labels":{"app":"guestbook-canary"}},"spec":{"containers":[{"image":"gcr.io/heptio-images/ks-guestbook-demo:0.1","name":"guestbook-canary","ports":[{"containerPort":80}]}]}}}}
|
||||
rollout.argoproj.io/revision: '2'
|
||||
clusterName: ''
|
||||
creationTimestamp: '2019-05-01T21:55:30Z'
|
||||
generation: 1
|
||||
labels:
|
||||
app.kubernetes.io/instance: guestbook-canary
|
||||
ksonnet.io/component: guestbook-ui
|
||||
name: guestbook-canary
|
||||
namespace: default
|
||||
resourceVersion: '956205'
|
||||
selfLink: /apis/argoproj.io/v1alpha1/namespaces/default/rollouts/guestbook-canary
|
||||
uid: d6105ccd-6c5b-11e9-b8d7-025000000001
|
||||
spec:
|
||||
minReadySeconds: 10
|
||||
replicas: 5
|
||||
selector:
|
||||
matchLabels:
|
||||
app: guestbook-canary
|
||||
strategy:
|
||||
canary:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
steps: []
|
||||
template:
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
app: guestbook-canary
|
||||
spec:
|
||||
containers:
|
||||
- image: 'gcr.io/heptio-images/ks-guestbook-demo:0.2'
|
||||
name: guestbook-canary
|
||||
ports:
|
||||
- containerPort: 80
|
||||
resources: {}
|
||||
status:
|
||||
HPAReplicas: 5
|
||||
availableReplicas: 5
|
||||
blueGreen: {}
|
||||
canary:
|
||||
stableRS: 567dd56d89
|
||||
conditions:
|
||||
- lastTransitionTime: '2019-05-01T22:00:16Z'
|
||||
lastUpdateTime: '2019-05-01T22:00:16Z'
|
||||
message: Rollout has minimum availability
|
||||
reason: AvailableReason
|
||||
status: 'True'
|
||||
type: Available
|
||||
- lastTransitionTime: '2019-05-01T21:55:30Z'
|
||||
lastUpdateTime: '2019-05-01T22:00:16Z'
|
||||
message: ReplicaSet "guestbook-canary-567dd56d89" has successfully progressed.
|
||||
reason: NewReplicaSetAvailable
|
||||
status: 'True'
|
||||
type: Progressing
|
||||
currentPodHash: 567dd56d89
|
||||
currentStepHash: 6c9545789c
|
||||
observedGeneration: 6886f85bff
|
||||
readyReplicas: 5
|
||||
replicas: 5
|
||||
selector: app=guestbook-canary
|
||||
updatedReplicas: 5
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/argoproj/argo-cd/server/rbacpolicy"
|
||||
"github.com/argoproj/argo-cd/util"
|
||||
"github.com/argoproj/argo-cd/util/cache"
|
||||
"github.com/argoproj/argo-cd/util/clusterauth"
|
||||
"github.com/argoproj/argo-cd/util/db"
|
||||
"github.com/argoproj/argo-cd/util/kube"
|
||||
"github.com/argoproj/argo-cd/util/rbac"
|
||||
@@ -174,6 +175,55 @@ func (s *Server) Delete(ctx context.Context, q *cluster.ClusterQuery) (*cluster.
|
||||
return &cluster.ClusterResponse{}, err
|
||||
}
|
||||
|
||||
// RotateAuth rotates the bearer token used for a cluster
|
||||
func (s *Server) RotateAuth(ctx context.Context, q *cluster.ClusterQuery) (*cluster.ClusterResponse, error) {
|
||||
if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceClusters, rbacpolicy.ActionUpdate, q.Server); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logCtx := log.WithField("cluster", q.Server)
|
||||
logCtx.Info("Rotating auth")
|
||||
clust, err := s.db.GetCluster(ctx, q.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
restCfg := clust.RESTConfig()
|
||||
if restCfg.BearerToken == "" {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "Cluster '%s' does not use bearer token authentication", q.Server)
|
||||
}
|
||||
claims, err := clusterauth.ParseServiceAccountToken(restCfg.BearerToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
kubeclientset, err := kubernetes.NewForConfig(restCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newSecret, err := clusterauth.GenerateNewClusterManagerSecret(kubeclientset, claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// we are using token auth, make sure we don't store client-cert information
|
||||
clust.Config.KeyData = nil
|
||||
clust.Config.CertData = nil
|
||||
clust.Config.BearerToken = string(newSecret.Data["token"])
|
||||
|
||||
// Test the token we just created before persisting it
|
||||
err = kube.TestConfig(clust.RESTConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = s.db.UpdateCluster(ctx, clust)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = clusterauth.RotateServiceAccountSecrets(kubeclientset, claims, newSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logCtx.Infof("Rotated auth (old: %s, new: %s)", claims.SecretName, newSecret.Name)
|
||||
return &cluster.ClusterResponse{}, nil
|
||||
}
|
||||
|
||||
func redact(clust *appv1.Cluster) *appv1.Cluster {
|
||||
if clust == nil {
|
||||
return nil
|
||||
|
||||
@@ -62,4 +62,9 @@ service ClusterService {
|
||||
option (google.api.http).delete = "/api/v1/clusters/{server}";
|
||||
}
|
||||
|
||||
// RotateAuth returns a cluster by server address
|
||||
rpc RotateAuth(ClusterQuery) returns (ClusterResponse) {
|
||||
option (google.api.http).post = "/api/v1/clusters/{server}/rotate-auth";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
21
test/e2e/cluster_objects_test.go
Normal file
21
test/e2e/cluster_objects_test.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
|
||||
)
|
||||
|
||||
// ensure that cluster scoped objects, like a cluster role, as a hok, can be successfully deployed
|
||||
func TestClusterRoleBinding(t *testing.T) {
|
||||
Given(t).
|
||||
Path("cluster-role").
|
||||
When().
|
||||
Create().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded)).
|
||||
Expect(HealthIs(HealthStatusHealthy)).
|
||||
Expect(SyncStatusIs(SyncStatusCodeSynced))
|
||||
}
|
||||
41
test/e2e/sync_options_test.go
Normal file
41
test/e2e/sync_options_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
|
||||
. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
|
||||
)
|
||||
|
||||
// TestSyncOptionsValidateFalse verifies we can disable validation during kubectl apply, using the
|
||||
// 'argocd.argoproj.io/sync-options: Validate=false' sync option
|
||||
func TestSyncOptionsValidateFalse(t *testing.T) {
|
||||
Given(t).
|
||||
Path("sync-options-validate-false").
|
||||
When().
|
||||
Create().
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationSucceeded))
|
||||
// NOTE: it is a bug that we do not detect this as OutOfSync. This is because we
|
||||
// are dropping fields as part of remarshalling. See: https://github.com/argoproj/argo-cd/issues/1787
|
||||
// Expect(SyncStatusIs(SyncStatusCodeOutOfSync))
|
||||
}
|
||||
|
||||
// TestSyncOptionsValidateTrue verifies when 'argocd.argoproj.io/sync-options: Validate=false' is
|
||||
// not present, then validation is performed and we fail during the apply
|
||||
func TestSyncOptionsValidateTrue(t *testing.T) {
|
||||
// k3s does not validate at all, so this test does not work
|
||||
if os.Getenv("ARGOCD_E2E_K3S") == "true" {
|
||||
t.SkipNow()
|
||||
}
|
||||
Given(t).
|
||||
Path("sync-options-validate-false").
|
||||
When().
|
||||
Create().
|
||||
PatchFile("invalid-cm.yaml", `[{"op": "remove", "path": "/metadata/annotations"}]`).
|
||||
Sync().
|
||||
Then().
|
||||
Expect(OperationPhaseIs(OperationFailed))
|
||||
}
|
||||
15
test/e2e/testdata/cluster-role/cluster-role.yaml
vendored
Normal file
15
test/e2e/testdata/cluster-role/cluster-role.yaml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
namespace: cert-manager
|
||||
name: my-cluster-role-binding
|
||||
annotations:
|
||||
argocd.argoproj.io/hook: PreSync
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: cluster-admin
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: default
|
||||
namespace: default
|
||||
12
test/e2e/testdata/cluster-role/pod.yaml
vendored
Normal file
12
test/e2e/testdata/cluster-role/pod.yaml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: my-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: main
|
||||
image: alpine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- "true"
|
||||
restartPolicy: Never
|
||||
8
test/e2e/testdata/sync-options-validate-false/invalid-cm.yaml
vendored
Normal file
8
test/e2e/testdata/sync-options-validate-false/invalid-cm.yaml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# This configmap fails when running `kubectl apply`, but succeeds when running `kubectl apply --validate=false`
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: invalid-cm
|
||||
annotations:
|
||||
argocd.argoproj.io/sync-options: Validate=false
|
||||
invalidKey: this-fails
|
||||
@@ -246,7 +246,7 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{ na
|
||||
services.repos.appDetails(src.repoURL, src.path, src.targetRevision, src.details)
|
||||
.catch(() => ({ type: 'Directory' as appModels.AppSourceType, path: application.spec.source.path }))}>
|
||||
{(details: appModels.RepoAppDetails) => <ApplicationParameters
|
||||
save={(app) => services.applications.updateSpec(app.metadata.name, app.spec)} application={application} details={details} />}
|
||||
save={(app) => this.updateApp(app)} application={application} details={details} />}
|
||||
</DataLoader>
|
||||
),
|
||||
}, {
|
||||
@@ -301,11 +301,11 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{ na
|
||||
iconClassName: 'fa fa-sync',
|
||||
title: <span className='show-for-medium'>Sync</span>,
|
||||
action: () => this.showDeploy('all'),
|
||||
}, {
|
||||
}, ...(application.status.operationState && [{
|
||||
iconClassName: 'fa fa-info-circle',
|
||||
title: <span className='show-for-medium'>Sync Status</span>,
|
||||
action: () => this.setOperationStatusVisible(true),
|
||||
}, {
|
||||
action: () => this.setOperationStatusVisible(true),
|
||||
}] || []), {
|
||||
iconClassName: 'fa fa-history',
|
||||
title: <span className='show-for-medium'>History and rollback</span>,
|
||||
action: () => this.setRollbackPanelVisible(0),
|
||||
@@ -370,15 +370,8 @@ export class ApplicationDetails extends React.Component<RouteComponentProps<{ na
|
||||
}
|
||||
|
||||
private async updateApp(app: appModels.Application) {
|
||||
try {
|
||||
await services.applications.updateSpec(app.metadata.name, app.spec);
|
||||
this.refreshRequested.next({});
|
||||
} catch (e) {
|
||||
this.appContext.apis.notifications.show({
|
||||
content: <ErrorNotification title='Unable to update application' e={e}/>,
|
||||
type: NotificationType.Error,
|
||||
});
|
||||
}
|
||||
await services.applications.updateSpec(app.metadata.name, app.spec);
|
||||
this.refreshRequested.next({});
|
||||
}
|
||||
|
||||
private groupAppNodesByKey(application: appModels.Application, tree: appModels.ApplicationTree) {
|
||||
|
||||
@@ -126,7 +126,7 @@ export const ApplicationOperationState: React.StatelessComponent<Props> = ({appl
|
||||
</div>
|
||||
<div className='columns small-1'>
|
||||
<utils.ResourceResultIcon
|
||||
resource={resource}/> {resource.status || resource.hookPhase}
|
||||
resource={resource}/> {resource.hookType ? resource.hookPhase : resource.status}
|
||||
</div>
|
||||
<div className='columns small-1'>
|
||||
{resource.hookType}
|
||||
|
||||
@@ -62,6 +62,7 @@ export const ApplicationSummary = (props: {
|
||||
<i className='fa fa-external-link'/> {app.spec.source.repoURL}
|
||||
</a>
|
||||
),
|
||||
edit: (formApi: FormApi) => <FormField formApi={formApi} field='spec.source.repoURL' component={Text}/>,
|
||||
},
|
||||
{
|
||||
title: 'TARGET REVISION',
|
||||
|
||||
@@ -171,7 +171,7 @@ export const ResourceResultIcon = ({resource}: { resource: appModels.ResourceRes
|
||||
let color = COLORS.sync_result.unknown;
|
||||
let icon = 'fa-question-circle';
|
||||
|
||||
if (resource.status) {
|
||||
if (!resource.hookType && resource.status) {
|
||||
switch (resource.status) {
|
||||
case appModels.ResultCodes.Synced:
|
||||
color = COLORS.sync_result.synced;
|
||||
@@ -195,7 +195,7 @@ export const ResourceResultIcon = ({resource}: { resource: appModels.ResourceRes
|
||||
}
|
||||
return <i title={title} className={'fa ' + icon} style={{ color }} />;
|
||||
}
|
||||
if (resource.hookPhase) {
|
||||
if (resource.hookType && resource.hookPhase) {
|
||||
let className = '';
|
||||
switch (resource.hookPhase) {
|
||||
case appModels.OperationPhases.Running:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import * as deepMerge from 'deepmerge';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import * as models from '../models';
|
||||
@@ -167,16 +168,19 @@ export class ApplicationsService {
|
||||
}
|
||||
|
||||
private parseAppFields(data: any): models.Application {
|
||||
const app = data as models.Application;
|
||||
app.kind = app.kind || 'Application';
|
||||
if (app.spec) {
|
||||
app.spec.project = app.spec.project || 'default';
|
||||
delete app.spec.source.componentParameterOverrides;
|
||||
}
|
||||
if (app.status) {
|
||||
app.status.resources = app.status.resources || [];
|
||||
app.status.summary = app.status.summary || {};
|
||||
}
|
||||
return app;
|
||||
data = deepMerge({
|
||||
apiVersion: 'argoproj.io/v1alpha1',
|
||||
kind: 'Application',
|
||||
spec: {
|
||||
project: 'default',
|
||||
},
|
||||
status: {
|
||||
resources: [],
|
||||
summary: {},
|
||||
},
|
||||
}, data);
|
||||
delete data.spec.source.componentParameterOverrides;
|
||||
|
||||
return data as models.Application;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package common
|
||||
package clusterauth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
log "github.com/sirupsen/logrus"
|
||||
apiv1 "k8s.io/api/core/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -39,7 +41,7 @@ func CreateServiceAccount(
|
||||
serviceAccountName string,
|
||||
namespace string,
|
||||
) error {
|
||||
serviceAccount := apiv1.ServiceAccount{
|
||||
serviceAccount := corev1.ServiceAccount{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ServiceAccount",
|
||||
@@ -153,7 +155,7 @@ func InstallClusterManagerRBAC(clientset kubernetes.Interface, ns string) (strin
|
||||
return "", err
|
||||
}
|
||||
|
||||
var serviceAccount *apiv1.ServiceAccount
|
||||
var serviceAccount *corev1.ServiceAccount
|
||||
var secretName string
|
||||
err = wait.Poll(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
serviceAccount, err = clientset.CoreV1().ServiceAccounts(ns).Get(ArgoCDManagerServiceAccount, metav1.GetOptions{})
|
||||
@@ -215,3 +217,106 @@ func UninstallRBAC(clientset kubernetes.Interface, namespace, bindingName, roleN
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServiceAccountClaims struct {
|
||||
Sub string `json:"sub"`
|
||||
Iss string `json:"iss"`
|
||||
Namespace string `json:"kubernetes.io/serviceaccount/namespace"`
|
||||
SecretName string `json:"kubernetes.io/serviceaccount/secret.name"`
|
||||
ServiceAccountName string `json:"kubernetes.io/serviceaccount/service-account.name"`
|
||||
ServiceAccountUID string `json:"kubernetes.io/serviceaccount/service-account.uid"`
|
||||
}
|
||||
|
||||
// Valid satisfies the jwt.Claims interface to enable JWT parsing
|
||||
func (sac *ServiceAccountClaims) Valid() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ParseServiceAccountToken parses a Kubernetes service account token
|
||||
func ParseServiceAccountToken(token string) (*ServiceAccountClaims, error) {
|
||||
parser := &jwt.Parser{
|
||||
SkipClaimsValidation: true,
|
||||
}
|
||||
var claims ServiceAccountClaims
|
||||
_, _, err := parser.ParseUnverified(token, &claims)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to parse service account token: %s", err)
|
||||
}
|
||||
return &claims, nil
|
||||
}
|
||||
|
||||
// GenerateNewClusterManagerSecret creates a new secret derived with same metadata as existing one
|
||||
// and waits until the secret is populated with a bearer token
|
||||
func GenerateNewClusterManagerSecret(clientset kubernetes.Interface, claims *ServiceAccountClaims) (*corev1.Secret, error) {
|
||||
secretsClient := clientset.CoreV1().Secrets(claims.Namespace)
|
||||
existingSecret, err := secretsClient.Get(claims.SecretName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var newSecret corev1.Secret
|
||||
secretNameSplit := strings.Split(claims.SecretName, "-")
|
||||
if len(secretNameSplit) > 0 {
|
||||
secretNameSplit = secretNameSplit[:len(secretNameSplit)-1]
|
||||
}
|
||||
newSecret.Type = corev1.SecretTypeServiceAccountToken
|
||||
newSecret.GenerateName = strings.Join(secretNameSplit, "-") + "-"
|
||||
newSecret.Annotations = existingSecret.Annotations
|
||||
// We will create an empty secret and let kubernetes populate the data
|
||||
newSecret.Data = nil
|
||||
|
||||
created, err := secretsClient.Create(&newSecret)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = wait.Poll(500*time.Millisecond, 30*time.Second, func() (bool, error) {
|
||||
created, err = secretsClient.Get(created.Name, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(created.Data) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Timed out waiting for secret to generate new token")
|
||||
}
|
||||
return created, nil
|
||||
}
|
||||
|
||||
// RotateServiceAccountSecrets rotates the entries in the service accounts secrets list
|
||||
func RotateServiceAccountSecrets(clientset kubernetes.Interface, claims *ServiceAccountClaims, newSecret *corev1.Secret) error {
|
||||
// 1. update service account secrets list with new secret name while also removing the old name
|
||||
saClient := clientset.CoreV1().ServiceAccounts(claims.Namespace)
|
||||
sa, err := saClient.Get(claims.ServiceAccountName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var newSecretsList []corev1.ObjectReference
|
||||
alreadyPresent := false
|
||||
for _, objRef := range sa.Secrets {
|
||||
if objRef.Name == claims.SecretName {
|
||||
continue
|
||||
}
|
||||
if objRef.Name == newSecret.Name {
|
||||
alreadyPresent = true
|
||||
}
|
||||
newSecretsList = append(newSecretsList, objRef)
|
||||
}
|
||||
if !alreadyPresent {
|
||||
sa.Secrets = append(newSecretsList, corev1.ObjectReference{Name: newSecret.Name})
|
||||
}
|
||||
_, err = saClient.Update(sa)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. delete existing secret object
|
||||
secretsClient := clientset.CoreV1().Secrets(claims.Namespace)
|
||||
err = secretsClient.Delete(claims.SecretName, &metav1.DeleteOptions{})
|
||||
if !apierr.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
102
util/clusterauth/clusterauth_test.go
Normal file
102
util/clusterauth/clusterauth_test.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package clusterauth
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierr "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
kubetesting "k8s.io/client-go/testing"
|
||||
|
||||
"github.com/argoproj/argo-cd/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
testToken = "eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhcmdvY2QtbWFuYWdlci10b2tlbi10ajc5ciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJhcmdvY2QtbWFuYWdlciIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjkxZGQzN2NmLThkOTItMTFlOS1hMDkxLWQ2NWYyYWU3ZmE4ZCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTphcmdvY2QtbWFuYWdlciJ9.ytZjt2pDV8-A7DBMR06zQ3wt9cuVEfq262TQw7sdra-KRpDpMPnziMhc8bkwvgW-LGhTWUh5iu1y-1QhEx6mtbCt7vQArlBRxfvM5ys6ClFkplzq5c2TtZ7EzGSD0Up7tdxuG9dvR6TGXYdfFcG779yCdZo2H48sz5OSJfdEriduMEY1iL5suZd3ebOoVi1fGflmqFEkZX6SvxkoArl5mtNP6TvZ1eTcn64xh4ws152hxio42E-eSnl_CET4tpB5vgP5BVlSKW2xB7w2GJxqdETA5LJRI_OilY77dTOp8cMr_Ck3EOeda3zHfh4Okflg8rZFEeAuJYahQNeAILLkcA"
|
||||
)
|
||||
|
||||
var (
|
||||
testClaims = ServiceAccountClaims{
|
||||
Sub: "system:serviceaccount:kube-system:argocd-manager",
|
||||
Iss: "kubernetes/serviceaccount",
|
||||
Namespace: "kube-system",
|
||||
SecretName: "argocd-manager-token-tj79r",
|
||||
ServiceAccountName: "argocd-manager",
|
||||
ServiceAccountUID: "91dd37cf-8d92-11e9-a091-d65f2ae7fa8d",
|
||||
}
|
||||
)
|
||||
|
||||
func newServiceAccount() *corev1.ServiceAccount {
|
||||
saBytes, err := ioutil.ReadFile("./testdata/argocd-manager-sa.yaml")
|
||||
errors.CheckError(err)
|
||||
var sa corev1.ServiceAccount
|
||||
err = yaml.Unmarshal(saBytes, &sa)
|
||||
errors.CheckError(err)
|
||||
return &sa
|
||||
}
|
||||
|
||||
func newServiceAccountSecret() *corev1.Secret {
|
||||
secretBytes, err := ioutil.ReadFile("./testdata/argocd-manager-sa-token.yaml")
|
||||
errors.CheckError(err)
|
||||
var secret corev1.Secret
|
||||
err = yaml.Unmarshal(secretBytes, &secret)
|
||||
errors.CheckError(err)
|
||||
return &secret
|
||||
}
|
||||
|
||||
func TestParseServiceAccountToken(t *testing.T) {
|
||||
claims, err := ParseServiceAccountToken(testToken)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testClaims, *claims)
|
||||
}
|
||||
|
||||
func TestGenerateNewClusterManagerSecret(t *testing.T) {
|
||||
kubeclientset := fake.NewSimpleClientset(newServiceAccountSecret())
|
||||
kubeclientset.ReactionChain = nil
|
||||
|
||||
generatedSecret := newServiceAccountSecret()
|
||||
generatedSecret.Name = "argocd-manager-token-abc123"
|
||||
generatedSecret.Data = map[string][]byte{
|
||||
"token": []byte("fake-token"),
|
||||
}
|
||||
|
||||
kubeclientset.AddReactor("*", "secrets", func(action kubetesting.Action) (handled bool, ret runtime.Object, err error) {
|
||||
return true, generatedSecret, nil
|
||||
})
|
||||
|
||||
created, err := GenerateNewClusterManagerSecret(kubeclientset, &testClaims)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "argocd-manager-token-abc123", created.Name)
|
||||
assert.Equal(t, "fake-token", string(created.Data["token"]))
|
||||
}
|
||||
|
||||
func TestRotateServiceAccountSecrets(t *testing.T) {
|
||||
generatedSecret := newServiceAccountSecret()
|
||||
generatedSecret.Name = "argocd-manager-token-abc123"
|
||||
generatedSecret.Data = map[string][]byte{
|
||||
"token": []byte("fake-token"),
|
||||
}
|
||||
|
||||
kubeclientset := fake.NewSimpleClientset(newServiceAccount(), newServiceAccountSecret(), generatedSecret)
|
||||
|
||||
err := RotateServiceAccountSecrets(kubeclientset, &testClaims, generatedSecret)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Verify service account references new secret and old secret is deleted
|
||||
saClient := kubeclientset.CoreV1().ServiceAccounts(testClaims.Namespace)
|
||||
sa, err := saClient.Get(testClaims.ServiceAccountName, metav1.GetOptions{})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, sa.Secrets, []corev1.ObjectReference{
|
||||
{
|
||||
Name: "argocd-manager-token-abc123",
|
||||
},
|
||||
})
|
||||
secretsClient := kubeclientset.CoreV1().Secrets(testClaims.Namespace)
|
||||
_, err = secretsClient.Get(testClaims.SecretName, metav1.GetOptions{})
|
||||
assert.True(t, apierr.IsNotFound(err))
|
||||
}
|
||||
18
util/clusterauth/testdata/argocd-manager-sa-token.yaml
vendored
Normal file
18
util/clusterauth/testdata/argocd-manager-sa-token.yaml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
data:
|
||||
ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM1ekNDQWMrZ0F3SUJBZ0lCQVRBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwdGFXNXAKYTNWaVpVTkJNQjRYRFRFNE1USXdOakF4TXpnME1Gb1hEVEk0TVRJd05EQXhNemcwTUZvd0ZURVRNQkVHQTFVRQpBeE1LYldsdWFXdDFZbVZEUVRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBS2RICkxWSGkwSnh4M1dYVkpueVdJck15djJZUThPWll5YzJpSHBSSVZ4eHlGdENnTVJqVEo1T3IxcTVoUG9XeGhrb1YKeFduUThWWFBGdlpNNUtTcS9Ocis5UGJ2WHlrdFdBaDZaYkVrM2s3S29taXorQk9CSjhMdkh6OHNicDRMQ2VnZwpHLzZ4aGRNWlNUL1VhNlFYbjhTQzBoRFBTSE4vdVpDb1dxWHlqdE5sdnJCeU81di9LZ3dXWjkvcGFnbmtmck1sCk5Qemh5Q2taK0pHSTR5THBtc3VBMnBYMTQrRXdhY2N1OGZmWUhOYitkMnJnZWltSTFmNytPaGRHRUtlTG5lamEKQm90NTZodnpYRWNoTjRJNS9nOU1CbXZOenhabndPbmVrUllOVDQvTHlwaUJEZU5UR1JlZWhybHlzaUVBcldJQgpPb3U0ZFBYbE5RblVmYTBPVVprQ0F3RUFBYU5DTUVBd0RnWURWUjBQQVFIL0JBUURBZ0trTUIwR0ExVWRKUVFXCk1CUUdDQ3NHQVFVRkJ3TUNCZ2dyQmdFRkJRY0RBVEFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQTBHQ1NxR1NJYjMKRFFFQkN3VUFBNElCQVFBTldRcUE1Q2UrR1N4WVVmNTg4bm91ZzNhZVJZZnBLZXIvSnlvazM4TzFKeFlLK2IydApxWGtIdFU0VmpRWFdGNEp6RG9sMlo1bDRSYzRVUWl5QlVUQk1ieS8vY2NGUnVYcER5R3ROQTNnU1hURG9YMjkzCk5SUUlPZndDTlFDUEJjbEpCN3d5YzRqZlZLWWxheXpkOGRuN0V6LzhNNmJXYUlIRWwxcEd0L21XZXZMZXoxUjQKdEhzbXA2RlY5d1lIckFaQyttaFMzOUVFc1lBRjBBdlVtUkFseU5GN1J4ZVdzRG14ZVVDUG9iQnd2Z0ppeGdJWQpqakZiWEk1ang1cEVlSnZnTVcvQmFMRHNpQlVWVnMvZnYzRGdjZkwzMm0zR1hiUHVzRHN0OGs1ZmYvWjV1UkdVCkJaVGtFeUxuUG9vM1pVRDhXZmI5T2x1MXhNSklnYlY5d3NuYwotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
||||
namespace: a3ViZS1zeXN0ZW0=
|
||||
token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklpSjkuZXlKcGMzTWlPaUpyZFdKbGNtNWxkR1Z6TDNObGNuWnBZMlZoWTJOdmRXNTBJaXdpYTNWaVpYSnVaWFJsY3k1cGJ5OXpaWEoyYVdObFlXTmpiM1Z1ZEM5dVlXMWxjM0JoWTJVaU9pSnJkV0psTFhONWMzUmxiU0lzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVmpjbVYwTG01aGJXVWlPaUpoY21kdlkyUXRiV0Z1WVdkbGNpMTBiMnRsYmkxMGFqYzVjaUlzSW10MVltVnlibVYwWlhNdWFXOHZjMlZ5ZG1salpXRmpZMjkxYm5RdmMyVnlkbWxqWlMxaFkyTnZkVzUwTG01aGJXVWlPaUpoY21kdlkyUXRiV0Z1WVdkbGNpSXNJbXQxWW1WeWJtVjBaWE11YVc4dmMyVnlkbWxqWldGalkyOTFiblF2YzJWeWRtbGpaUzFoWTJOdmRXNTBMblZwWkNJNklqa3haR1F6TjJObUxUaGtPVEl0TVRGbE9TMWhNRGt4TFdRMk5XWXlZV1UzWm1FNFpDSXNJbk4xWWlJNkluTjVjM1JsYlRwelpYSjJhV05sWVdOamIzVnVkRHByZFdKbExYTjVjM1JsYlRwaGNtZHZZMlF0YldGdVlXZGxjaUo5Lnl0Wmp0MnBEVjgtQTdEQk1SMDZ6UTN3dDljdVZFZnEyNjJUUXc3c2RyYS1LUnBEcE1QbnppTWhjOGJrd3ZnVy1MR2hUV1VoNWl1MXktMVFoRXg2bXRiQ3Q3dlFBcmxCUnhmdk01eXM2Q2xGa3BsenE1YzJUdFo3RXpHU0QwVXA3dGR4dUc5ZHZSNlRHWFlkZkZjRzc3OXlDZFpvMkg0OHN6NU9TSmZkRXJpZHVNRVkxaUw1c3VaZDNlYk9vVmkxZkdmbG1xRkVrWlg2U3Z4a29Bcmw1bXROUDZUdloxZVRjbjY0eGg0d3MxNTJoeGlvNDJFLWVTbmxfQ0VUNHRwQjV2Z1A1QlZsU0tXMnhCN3cyR0p4cWRFVEE1TEpSSV9PaWxZNzdkVE9wOGNNcl9DazNFT2VkYTN6SGZoNE9rZmxnOHJaRkVlQXVKWWFoUU5lQUlMTGtjQQ==
|
||||
kind: Secret
|
||||
metadata:
|
||||
annotations:
|
||||
kubernetes.io/service-account.name: argocd-manager
|
||||
kubernetes.io/service-account.uid: 91dd37cf-8d92-11e9-a091-d65f2ae7fa8d
|
||||
creationTimestamp: "2019-06-13T04:30:24Z"
|
||||
generateName: argocd-manager-token-
|
||||
name: argocd-manager-token-tj79r
|
||||
namespace: kube-system
|
||||
resourceVersion: "133010"
|
||||
selfLink: /api/v1/namespaces/kube-system/secrets/argocd-manager-token-tj79r
|
||||
uid: f657d67e-8d93-11e9-a091-d65f2ae7fa8d
|
||||
type: kubernetes.io/service-account-token
|
||||
11
util/clusterauth/testdata/argocd-manager-sa.yaml
vendored
Normal file
11
util/clusterauth/testdata/argocd-manager-sa.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
creationTimestamp: "2019-06-13T04:20:26Z"
|
||||
name: argocd-manager
|
||||
namespace: kube-system
|
||||
resourceVersion: "133015"
|
||||
selfLink: /api/v1/namespaces/kube-system/serviceaccounts/argocd-manager
|
||||
uid: 91dd37cf-8d92-11e9-a091-d65f2ae7fa8d
|
||||
secrets:
|
||||
- name: argocd-manager-token-tj79r
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/yudai/gojsondiff"
|
||||
@@ -36,11 +37,11 @@ type Normalizer interface {
|
||||
// "kubectl.kubernetes.io/last-applied-configuration", then perform a three way diff.
|
||||
func Diff(config, live *unstructured.Unstructured, normalizer Normalizer) *DiffResult {
|
||||
if config != nil {
|
||||
config = stripTypeInformation(config)
|
||||
config = remarshal(config)
|
||||
Normalize(config, normalizer)
|
||||
}
|
||||
if live != nil {
|
||||
live = stripTypeInformation(live)
|
||||
live = remarshal(live)
|
||||
Normalize(live, normalizer)
|
||||
}
|
||||
orig := GetLastAppliedConfigAnnotation(live)
|
||||
@@ -439,3 +440,39 @@ func toString(val interface{}) string {
|
||||
}
|
||||
return fmt.Sprintf("%s", val)
|
||||
}
|
||||
|
||||
// remarshal checks resource kind and version and re-marshal using corresponding struct custom marshaller.
|
||||
// This ensures that expected resource state is formatter same as actual resource state in kubernetes
|
||||
// and allows to find differences between actual and target states more accurately.
|
||||
// Remarshalling also strips any type information (e.g. float64 vs. int) from the unstructured
|
||||
// object. This is important for diffing since it will cause godiff to report a false difference.
|
||||
func remarshal(obj *unstructured.Unstructured) *unstructured.Unstructured {
|
||||
obj = stripTypeInformation(obj)
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
gvk := obj.GroupVersionKind()
|
||||
item, err := scheme.Scheme.New(obj.GroupVersionKind())
|
||||
if err != nil {
|
||||
// this is common. the scheme is not registered
|
||||
log.Debugf("Could not create new object of type %s: %v", gvk, err)
|
||||
return obj
|
||||
}
|
||||
// This will drop any omitempty fields, perform resource conversion etc...
|
||||
unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
|
||||
err = json.Unmarshal(data, &unmarshalledObj)
|
||||
if err != nil {
|
||||
// User may have specified an invalid spec in git. Return original object
|
||||
log.Warnf("Could not unmarshal to object of type %s: %v", gvk, err)
|
||||
return obj
|
||||
}
|
||||
unstrBody, err := runtime.DefaultUnstructuredConverter.ToUnstructured(unmarshalledObj)
|
||||
if err != nil {
|
||||
log.Warnf("Could not unmarshal to object of type %s: %v", gvk, err)
|
||||
return obj
|
||||
}
|
||||
// remove all default values specified by custom formatter (e.g. creationTimestamp)
|
||||
unstrBody = jsonutil.RemoveMapFields(obj.Object, unstrBody)
|
||||
return &unstructured.Unstructured{Object: unstrBody}
|
||||
}
|
||||
|
||||
@@ -587,3 +587,48 @@ func TestHideSecretDataLastAppliedConfig(t *testing.T) {
|
||||
assert.Equal(t, map[string]interface{}{"key1": replacement3}, secretData(lastAppliedSecret))
|
||||
|
||||
}
|
||||
|
||||
func TestRemarshal(t *testing.T) {
|
||||
manifest := []byte(`
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
imagePullSecrets: []
|
||||
metadata:
|
||||
name: my-sa
|
||||
`)
|
||||
var un unstructured.Unstructured
|
||||
err := yaml.Unmarshal(manifest, &un)
|
||||
assert.NoError(t, err)
|
||||
newUn := remarshal(&un)
|
||||
_, ok := newUn.Object["imagePullSecrets"]
|
||||
assert.False(t, ok)
|
||||
metadata := newUn.Object["metadata"].(map[string]interface{})
|
||||
_, ok = metadata["creationTimestamp"]
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestRemarshalResources(t *testing.T) {
|
||||
manifest := []byte(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: my-pod
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.7.9
|
||||
name: nginx
|
||||
resources:
|
||||
requests:
|
||||
cpu: 0.2
|
||||
`)
|
||||
un := unstructured.Unstructured{}
|
||||
err := yaml.Unmarshal(manifest, &un)
|
||||
assert.NoError(t, err)
|
||||
requestsBefore := un.Object["spec"].(map[string]interface{})["containers"].([]interface{})[0].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})
|
||||
log.Println(requestsBefore)
|
||||
newUn := remarshal(&un)
|
||||
requestsAfter := newUn.Object["spec"].(map[string]interface{})["containers"].([]interface{})[0].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})
|
||||
log.Println(requestsAfter)
|
||||
assert.Equal(t, float64(0.2), requestsBefore["cpu"])
|
||||
assert.Equal(t, "200m", requestsAfter["cpu"])
|
||||
}
|
||||
|
||||
@@ -3,9 +3,7 @@ package kube
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -27,10 +25,8 @@ import (
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
|
||||
"k8s.io/kubernetes/pkg/kubectl/scheme"
|
||||
|
||||
"github.com/argoproj/argo-cd/common"
|
||||
jsonutil "github.com/argoproj/argo-cd/util/json"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -369,44 +365,11 @@ func SplitYAML(out string) ([]*unstructured.Unstructured, error) {
|
||||
}
|
||||
continue
|
||||
}
|
||||
remObj, err := Remarshal(&obj)
|
||||
if err != nil {
|
||||
log.Debugf("Failed to remarshal oject: %v", err)
|
||||
} else {
|
||||
obj = *remObj
|
||||
}
|
||||
objs = append(objs, &obj)
|
||||
}
|
||||
return objs, firstErr
|
||||
}
|
||||
|
||||
// Remarshal checks resource kind and version and re-marshal using corresponding struct custom marshaller.
|
||||
// This ensures that expected resource state is formatter same as actual resource state in kubernetes
|
||||
// and allows to find differences between actual and target states more accurately.
|
||||
func Remarshal(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item, err := scheme.Scheme.New(obj.GroupVersionKind())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// This will drop any omitempty fields, perform resource conversion etc...
|
||||
unmarshalledObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
|
||||
err = json.Unmarshal(data, &unmarshalledObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
unstrBody, err := runtime.DefaultUnstructuredConverter.ToUnstructured(unmarshalledObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// remove all default values specified by custom formatter (e.g. creationTimestamp)
|
||||
unstrBody = jsonutil.RemoveMapFields(obj.Object, unstrBody)
|
||||
return &unstructured.Unstructured{Object: unstrBody}, nil
|
||||
}
|
||||
|
||||
// WatchWithRetry returns channel of watch events or errors of failed to call watch API.
|
||||
func WatchWithRetry(ctx context.Context, getWatch func() (watch.Interface, error)) chan struct {
|
||||
*watch.Event
|
||||
|
||||
@@ -202,53 +202,6 @@ func TestCleanKubectlOutput(t *testing.T) {
|
||||
assert.Equal(t, cleanKubectlOutput(testString), `error validating data: ValidationError(Deployment.spec): missing required field "selector" in io.k8s.api.apps.v1beta2.DeploymentSpec`)
|
||||
}
|
||||
|
||||
func TestRemarshal(t *testing.T) {
|
||||
manifest := []byte(`
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
imagePullSecrets: []
|
||||
metadata:
|
||||
name: my-sa
|
||||
`)
|
||||
var un unstructured.Unstructured
|
||||
err := yaml.Unmarshal(manifest, &un)
|
||||
assert.NoError(t, err)
|
||||
newUn, err := Remarshal(&un)
|
||||
assert.NoError(t, err)
|
||||
_, ok := newUn.Object["imagePullSecrets"]
|
||||
assert.False(t, ok)
|
||||
metadata := newUn.Object["metadata"].(map[string]interface{})
|
||||
_, ok = metadata["creationTimestamp"]
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestRemarshalResources(t *testing.T) {
|
||||
manifest := []byte(`
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: my-pod
|
||||
spec:
|
||||
containers:
|
||||
- image: nginx:1.7.9
|
||||
name: nginx
|
||||
resources:
|
||||
requests:
|
||||
cpu: 0.2
|
||||
`)
|
||||
un := unstructured.Unstructured{}
|
||||
err := yaml.Unmarshal(manifest, &un)
|
||||
assert.NoError(t, err)
|
||||
requestsBefore := un.Object["spec"].(map[string]interface{})["containers"].([]interface{})[0].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})
|
||||
log.Println(requestsBefore)
|
||||
newUn, err := Remarshal(&un)
|
||||
assert.NoError(t, err)
|
||||
requestsAfter := newUn.Object["spec"].(map[string]interface{})["containers"].([]interface{})[0].(map[string]interface{})["resources"].(map[string]interface{})["requests"].(map[string]interface{})
|
||||
log.Println(requestsAfter)
|
||||
assert.Equal(t, float64(0.2), requestsBefore["cpu"])
|
||||
assert.Equal(t, "200m", requestsAfter["cpu"])
|
||||
}
|
||||
|
||||
func TestInClusterKubeConfig(t *testing.T) {
|
||||
restConfig := &rest.Config{}
|
||||
kubeConfig := NewKubeConfig(restConfig, "")
|
||||
|
||||
Reference in New Issue
Block a user