Compare commits

...

12 Commits

Author SHA1 Message Date
Alex Collins
ad7a69a798 Adds flags for codegen and lint 2019-10-23 12:50:11 -07:00
Alex Collins
fc588538bc Increase test timeout to 10s as per master 2019-10-23 10:41:18 -07:00
Alexander Matyushentsev
675ae6a991 Issue #2185 - Manual sync don't trigger hooks (#2477) 2019-10-23 10:33:05 -07:00
Alex Collins
6cc0fd8b8c Increases lint deadline to 4m 2019-10-23 09:33:44 -07:00
Alexander Matyushentsev
26e1cf0438 Issue #2339 - Controller should compare with latest git revision if app has changed (#2543) 2019-10-22 15:28:23 -07:00
Alexander Matyushentsev
30d3fcb84a Unknown child app should not affect app health (#2544) 2019-10-22 15:28:17 -07:00
Alex Collins
3a3a7f5044 Update manifests to v1.2.4 2019-10-22 10:50:42 -07:00
Simon Behar
6c34dd828f Redact secrets in dex logs (#2538)
* Done

* Pre-commit

* Added test

* Pre-commit

* Goimports
2019-10-22 10:46:25 -07:00
Fred Dubois
de7003f530 Make argo-cd docker images openshift friendly (#2362)
In openshift clusters, the user id of your container can be arbitrary,
so you need to make the running images compatible with this behavior.

The problematic application for argo-cd was the repo server. When trying
to clone the repos it was getting the error "No user exists for uid
100083000" (100083000 was the random user id being injected by
openshift in my case). This was because the user 100083000 wasn't in the
/etc/passwd file.

The changes in this commit add a uid_entrypoint.sh script that, when the
container starts, modifies the /etc/passwd file to add an entry with the
current UID _only_ if the current UID isn't there.

References:
* Problematic behavior of ssh when user id isn't in the /etc/passwd file:
  https://unix.stackexchange.com/questions/524268/running-git-or-ssh-client-in-docker-as-user-no-user-exists-for-uid
* OpenShift guidelines on how to make your docker image runnable by
  arbitrary user ids:
  https://access.redhat.com/documentation/en-us/openshift_container_platform/3.11/html/creating_images/creating-images-guidelines#use-uid
2019-10-01 12:44:47 -07:00
Alexander Matyushentsev
8981903603 Update manifests to v1.2.3 2019-10-01 11:56:35 -07:00
Alexander Matyushentsev
509c567a40 Stop loggin /repository.RepositoryService/ValidateAccess parameters (#2387) 2019-10-01 11:55:00 -07:00
Alexander Matyushentsev
c722a71820 Add dest-server and dest-namespace field to reconciliation logs (#2388) 2019-10-01 11:54:55 -07:00
22 changed files with 182 additions and 43 deletions

View File

@@ -77,7 +77,9 @@ RUN echo 'deb http://deb.debian.org/debian stretch-backports main' >> /etc/apt/s
RUN groupadd -g 999 argocd && \
useradd -r -u 999 -g argocd argocd && \
mkdir -p /home/argocd && \
chown argocd:argocd /home/argocd && \
chown argocd:0 /home/argocd && \
chmod g=u /home/argocd && \
chmod g=u /etc/passwd && \
apt-get update && \
apt-get install -y git git-lfs && \
apt-get clean && \
@@ -89,6 +91,9 @@ COPY --from=builder /usr/local/bin/helm /usr/local/bin/helm
COPY --from=builder /usr/local/bin/kubectl /usr/local/bin/kubectl
COPY --from=builder /usr/local/bin/kustomize /usr/local/bin/kustomize
COPY --from=builder /usr/local/bin/aws-iam-authenticator /usr/local/bin/aws-iam-authenticator
# script to add current (possibly arbitrary) user to /etc/passwd at runtime
# (if it's not already there, to be openshift friendly)
COPY uid_entrypoint.sh /usr/local/bin/uid_entrypoint.sh
# support for mounting configuration from a configmap
RUN mkdir -p /app/config/ssh && \

View File

@@ -27,7 +27,9 @@ DEV_IMAGE?=false
LINT_GOGC?=off
LINT_CONCURRENCY?=8
# Set timeout for linter
LINT_DEADLINE?=1m0s
LINT_DEADLINE?=4m0s
CODEGEN=true
LINT=true
override LDFLAGS += \
-X ${PACKAGE}.version=${VERSION} \
@@ -74,7 +76,7 @@ codegen-local: protogen clientgen openapigen manifests-local
.PHONY: codegen
codegen: dev-tools-image
$(call run-in-dev-tool,make codegen-local)
@if [ "$(CODGEN)" = "true" ]; then $(call run-in-dev-tool,make codegen-local) ; fi
.PHONY: cli
cli: clean-debug
@@ -161,7 +163,7 @@ lint-local: build
.PHONY: lint
lint: dev-tools-image
$(call run-in-dev-tool,make lint-local LINT_CONCURRENCY=$(LINT_CONCURRENCY) LINT_DEADLINE=$(LINT_DEADLINE) LINT_GOGC=$(LINT_GOGC))
@if [ "$(LINT)" = "true" ]; then $(call run-in-dev-tool,make lint-local LINT_CONCURRENCY=$(LINT_CONCURRENCY) LINT_DEADLINE=$(LINT_DEADLINE) LINT_GOGC=$(LINT_GOGC)); fi
.PHONY: build
build:

View File

@@ -1 +1 @@
1.2.2
1.2.4

View File

@@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"regexp"
"syscall"
"github.com/ghodss/yaml"
@@ -108,7 +109,7 @@ func NewRunDexCommand() *cobra.Command {
} else {
err = ioutil.WriteFile("/tmp/dex.yaml", dexCfgBytes, 0644)
errors.CheckError(err)
log.Info(string(dexCfgBytes))
log.Info(redactor(string(dexCfgBytes)))
cmd = exec.Command("dex", "serve", "/tmp/dex.yaml")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
@@ -554,6 +555,11 @@ func NewClusterConfig() *cobra.Command {
return command
}
func redactor(dirtyString string) string {
dirtyString = regexp.MustCompile("(clientSecret: )[^ \n]*").ReplaceAllString(dirtyString, "$1********")
return regexp.MustCompile("(secret: )[^ \n]*").ReplaceAllString(dirtyString, "$1********")
}
func main() {
if err := NewCommand().Execute(); err != nil {
fmt.Println(err)

View File

@@ -0,0 +1,73 @@
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
var textToRedact = `
- config:
clientID: aabbccddeeff00112233
clientSecret: $dex.github.clientSecret
orgs:
- name: your-github-org
redirectURI: https://argocd.example.com/api/dex/callback
id: github
name: GitHub
type: github
grpc:
addr: 0.0.0.0:5557
issuer: https://argocd.example.com/api/dex
oauth2:
skipApprovalScreen: true
staticClients:
- id: argo-cd
name: Argo CD
redirectURIs:
- https://argocd.example.com/auth/callback
secret: Dis9M-GA11oTwZVQQWdDklPQw-sWXZkWJFyyEhMs
- id: argo-cd-cli
name: Argo CD CLI
public: true
redirectURIs:
- http://localhost
storage:
type: memory
web:
http: 0.0.0.0:5556`
var expectedRedaction = `
- config:
clientID: aabbccddeeff00112233
clientSecret: ********
orgs:
- name: your-github-org
redirectURI: https://argocd.example.com/api/dex/callback
id: github
name: GitHub
type: github
grpc:
addr: 0.0.0.0:5557
issuer: https://argocd.example.com/api/dex
oauth2:
skipApprovalScreen: true
staticClients:
- id: argo-cd
name: Argo CD
redirectURIs:
- https://argocd.example.com/auth/callback
secret: ********
- id: argo-cd-cli
name: Argo CD CLI
public: true
redirectURIs:
- http://localhost
storage:
type: memory
web:
http: 0.0.0.0:5556`
func TestSecretsRedactor(t *testing.T) {
assert.Equal(t, expectedRedaction, redactor(textToRedact))
}

View File

@@ -672,7 +672,13 @@ func (ctrl *ApplicationController) processAppRefreshQueueItem() (processNext boo
defer func() {
reconcileDuration := time.Since(startTime)
ctrl.metricsServer.IncReconcile(origApp, reconcileDuration)
logCtx := log.WithFields(log.Fields{"application": origApp.Name, "time_ms": reconcileDuration.Seconds() * 1e3, "level": comparisonLevel})
logCtx := log.WithFields(log.Fields{
"application": origApp.Name,
"time_ms": reconcileDuration.Seconds() * 1e3,
"level": comparisonLevel,
"dest-server": origApp.Spec.Destination.Server,
"dest-namespace": origApp.Spec.Destination.Namespace,
})
logCtx.Info("Reconciliation completed")
}()
@@ -756,23 +762,22 @@ func (ctrl *ApplicationController) needRefreshAppStatus(app *appv1.Application,
compareWith := CompareWithLatest
refreshType := appv1.RefreshTypeNormal
expired := app.Status.ReconciledAt == nil || app.Status.ReconciledAt.Add(statusRefreshTimeout).Before(time.Now().UTC())
if requestedType, ok := app.IsRefreshRequested(); ok || expired {
if ok {
refreshType = requestedType
reason = fmt.Sprintf("%s refresh requested", refreshType)
} else if expired {
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
}
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
compareWith = level
reason = fmt.Sprintf("controller refresh requested")
} else if app.Status.Sync.Status == appv1.SyncStatusCodeUnknown && expired {
reason = "comparison status unknown"
if requestedType, ok := app.IsRefreshRequested(); ok {
// user requested app refresh.
refreshType = requestedType
reason = fmt.Sprintf("%s refresh requested", refreshType)
} else if expired {
reason = fmt.Sprintf("comparison expired. reconciledAt: %v, expiry: %v", app.Status.ReconciledAt, statusRefreshTimeout)
} else if !app.Spec.Source.Equals(app.Status.Sync.ComparedTo.Source) {
reason = "spec.source differs"
} else if !app.Spec.Destination.Equals(app.Status.Sync.ComparedTo.Destination) {
reason = "spec.destination differs"
} else if requested, level := ctrl.isRefreshRequested(app.Name); requested {
compareWith = level
reason = fmt.Sprintf("controller refresh requested")
}
if reason != "" {
logCtx.Infof("Refreshing app status (%s), level (%d)", reason, compareWith)
return true, refreshType, compareWith

View File

@@ -550,4 +550,22 @@ func TestNeedRefreshAppStatus(t *testing.T) {
assert.Equal(t, argoappv1.RefreshTypeHard, refreshType)
assert.Equal(t, CompareWithLatest, compareWith)
}
{
app := app.DeepCopy()
// ensure that CompareWithLatest level is used if application source has changed
ctrl.requestAppRefresh(app.Name, ComparisonWithNothing)
// sample app source change
app.Spec.Source.Helm = &argoappv1.ApplicationSourceHelm{
Parameters: []argoappv1.HelmParameter{{
Name: "foo",
Value: "bar",
}},
}
needRefresh, refreshType, compareWith = ctrl.needRefreshAppStatus(app, 1*time.Hour)
assert.True(t, needRefresh)
assert.Equal(t, argoappv1.RefreshTypeNormal, refreshType)
assert.Equal(t, CompareWithLatest, compareWith)
}
}

View File

@@ -12,7 +12,7 @@ bases:
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: v1.2.2
newTag: v1.2.4
- name: argoproj/argocd-ui
newName: argoproj/argocd-ui
newTag: v1.2.2
newTag: v1.2.4

View File

@@ -21,6 +21,7 @@ spec:
image: argoproj/argocd:latest
imagePullPolicy: Always
command:
- uid_entrypoint.sh
- argocd-repo-server
- --redis
- argocd-redis:6379

View File

@@ -18,7 +18,7 @@ bases:
images:
- name: argoproj/argocd
newName: argoproj/argocd
newTag: v1.2.2
newTag: v1.2.4
- name: argoproj/argocd-ui
newName: argoproj/argocd-ui
newTag: v1.2.2
newTag: v1.2.4

View File

@@ -23,6 +23,7 @@ spec:
containers:
- name: argocd-repo-server
command:
- uid_entrypoint.sh
- argocd-repo-server
- --sentinel
- argocd-redis-ha-announce-0:26379

View File

@@ -2901,7 +2901,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2955,7 +2955,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -3001,6 +3001,7 @@ spec:
automountServiceAccountToken: false
containers:
- command:
- uid_entrypoint.sh
- argocd-repo-server
- --sentinel
- argocd-redis-ha-announce-0:26379
@@ -3010,7 +3011,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -3084,7 +3085,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2816,7 +2816,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2870,7 +2870,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2916,6 +2916,7 @@ spec:
automountServiceAccountToken: false
containers:
- command:
- uid_entrypoint.sh
- argocd-repo-server
- --sentinel
- argocd-redis-ha-announce-0:26379
@@ -2925,7 +2926,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2999,7 +3000,7 @@ spec:
- argocd-redis-ha-announce-2:26379
- --sentinelmaster
- argocd
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2665,7 +2665,7 @@ spec:
- "20"
- --operation-processors
- "10"
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2719,7 +2719,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2779,10 +2779,11 @@ spec:
automountServiceAccountToken: false
containers:
- command:
- uid_entrypoint.sh
- argocd-repo-server
- --redis
- argocd-redis:6379
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2833,7 +2834,7 @@ spec:
- argocd-server
- --staticassets
- /shared/app
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -2580,7 +2580,7 @@ spec:
- "20"
- --operation-processors
- "10"
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:
@@ -2634,7 +2634,7 @@ spec:
- cp
- /usr/local/bin/argocd-util
- /shared
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
name: copyutil
volumeMounts:
@@ -2694,10 +2694,11 @@ spec:
automountServiceAccountToken: false
containers:
- command:
- uid_entrypoint.sh
- argocd-repo-server
- --redis
- argocd-redis:6379
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
initialDelaySeconds: 5
@@ -2748,7 +2749,7 @@ spec:
- argocd-server
- --staticassets
- /shared/app
image: argoproj/argocd:v1.2.2
image: argoproj/argocd:v1.2.4
imagePullPolicy: Always
livenessProbe:
httpGet:

View File

@@ -419,6 +419,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
"/account.AccountService/UpdatePassword": true,
"/repository.RepositoryService/Create": true,
"/repository.RepositoryService/Update": true,
"/repository.RepositoryService/ValidateAccess": true,
"/application.ApplicationService/PatchResource": true,
}
// NOTE: notice we do not configure the gRPC server here with TLS (e.g. grpc.Creds(creds))

View File

@@ -35,7 +35,7 @@ type Context struct {
func Given(t *testing.T) *Context {
fixture.EnsureCleanState(t)
return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, repoURLType: fixture.RepoURLTypeFile, name: fixture.Name(), timeout: 5, project: "default", prune: true}
return &Context{t: t, destServer: KubernetesInternalAPIServerAddr, repoURLType: fixture.RepoURLTypeFile, name: fixture.Name(), timeout: 10, project: "default", prune: true}
}
func (c *Context) CustomCACertAdded() *Context {

View File

@@ -29,6 +29,7 @@ func TestPostSyncHookSuccessful(t *testing.T) {
// make sure we can run a standard sync hook
func testHookSuccessful(t *testing.T, hookType HookType, podHookPhase OperationPhase) {
Given(t).
Timeout(10).
Path("hook").
When().
PatchFile("hook.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "%s"}}]`, hookType)).

View File

@@ -16,7 +16,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {
const [form, setForm] = React.useState<FormApi>(null);
const isVisible = !!(selectedResource && application);
const appResources = (application && selectedResource && application.status && application.status.resources || []).sort(
(first, second) => nodeKey(first).localeCompare(nodeKey(second)));
(first, second) => nodeKey(first).localeCompare(nodeKey(second))).filter((item) => !item.hook);
const syncResIndex = appResources.findIndex((item) => nodeKey(item) === selectedResource);
const syncStrategy = {} as models.SyncStrategy;
@@ -36,7 +36,7 @@ export const ApplicationSyncPanel = ({application, selectedResource, hide}: {
<Form
defaultValues={{
revision: application.spec.source.targetRevision || 'HEAD',
resources: appResources.filter((item) => !item.hook).map((_, i) => i === syncResIndex || syncResIndex === -1),
resources: appResources.map((_, i) => i === syncResIndex || syncResIndex === -1),
}}
validateError={(values) => ({
resources: values.resources.every((item: boolean) => !item) && 'Select at least one resource',

12
uid_entrypoint.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
# Make sure that if we are using an arbitrary UID that it appears in /etc/passwd,
# otherwise this will cause issues with things like cloning with git+ssh
# reference: https://access.redhat.com/documentation/en-us/openshift_container_platform/3.11/html/creating_images/creating-images-guidelines#use-uid
if ! whoami &> /dev/null; then
if [ -w /etc/passwd ]; then
echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:/home/argocd:/sbin/nologin" >> /etc/passwd
fi
fi
exec "$@"

View File

@@ -63,7 +63,7 @@ func ignoreLiveObjectHealth(liveObj *unstructured.Unstructured, resHealth appv1.
return true
}
gvk := liveObj.GroupVersionKind()
if gvk.Group == "argoproj.io" && gvk.Kind == "Application" && resHealth.Status == appv1.HealthStatusMissing {
if gvk.Group == "argoproj.io" && gvk.Kind == "Application" && (resHealth.Status == appv1.HealthStatusMissing || resHealth.Status == appv1.HealthStatusUnknown) {
// Covers the app-of-apps corner case where child app is deployed but that app itself
// has a status of 'Missing', which we don't want to cause the parent's health status
// to also be Missing

View File

@@ -115,6 +115,7 @@ func TestAppOfAppsHealth(t *testing.T) {
}
missingApp, missingStatus := newAppLiveObj("foo", appv1.HealthStatusMissing)
unknownApp, unknownStatus := newAppLiveObj("fooz", appv1.HealthStatusUnknown)
healthyApp, healthyStatus := newAppLiveObj("bar", appv1.HealthStatusHealthy)
degradedApp, degradedStatus := newAppLiveObj("baz", appv1.HealthStatusDegraded)
@@ -127,6 +128,15 @@ func TestAppOfAppsHealth(t *testing.T) {
assert.Equal(t, appv1.HealthStatusHealthy, healthStatus.Status)
}
// verify unknown child app does not affect app health
{
unknownAndHealthyStatuses := []appv1.ResourceStatus{unknownStatus, healthyStatus}
unknownAndHealthyLiveObjects := []*unstructured.Unstructured{unknownApp, healthyApp}
healthStatus, err := SetApplicationHealth(unknownAndHealthyStatuses, unknownAndHealthyLiveObjects, nil, noFilter)
assert.NoError(t, err)
assert.Equal(t, appv1.HealthStatusHealthy, healthStatus.Status)
}
// verify degraded does affect
{
degradedAndHealthyStatuses := []appv1.ResourceStatus{degradedStatus, healthyStatus}