Compare commits

...

15 Commits

Author SHA1 Message Date
Jesse Suen
20693fd44c Bump version to v0.7.2, add release notes, update manifests 2018-08-21 00:16:15 -07:00
Jesse Suen
37bb65b0ac API discovery becomes best effort when partial resource list is returned (resolves #524) (#525) 2018-08-21 00:10:43 -07:00
Jesse Suen
da7be2e3ca Update manifests and install instructions for v0.7.1 (#496) 2018-08-03 13:39:00 -07:00
Alexander Matyushentsev
d138c10eb6 Fix 404 error in repo API (#495) 2018-08-03 12:54:33 -07:00
Alexander Matyushentsev
4af13eba60 Issue #474 - ListApps API does not scale (#494) 2018-08-03 20:10:38 +03:00
Alexander Matyushentsev
e998e499db Issue #476 - AppProjectSpec SourceRepos mislabeled (#490) 2018-08-02 20:59:05 +03:00
Alexander Matyushentsev
53cdced69b Issue #491 - Failed e2e test does not fail CI workflow (#492)
* Issue #491 - Fix broken e2e test

* Issue #491 - return e2e test exit code
2018-08-02 20:50:36 +03:00
ChocoPowwwa
e726da46a5 Fix linux download link in getting_started.md (#487)
fix typo
2018-08-02 00:04:21 -07:00
Jesse Suen
b0d6a7092e Fix failure in identifying app source type when path was '.' (#486) 2018-07-31 23:46:16 -07:00
Alexander Matyushentsev
1fe870c0d7 Issue #463 - Surface helm parameters to the application level (#485)
* Issue #463 - Surface helm parameters to the application level

* Move get helm params functionality to separate function

* Use github.com/ghodss/yaml in helm GetParameters
2018-08-01 09:44:56 +03:00
Jesse Suen
469cf1d164 Fix issue where application server was retrieving events from incorrect cluster (resolves #478) (#484) 2018-07-31 14:15:53 -07:00
Jesse Suen
00299707e5 Expand RBAC role to be able to create application events. Fix username claims extraction. (#479) 2018-07-31 11:15:44 -07:00
Jesse Suen
9f5a718323 Infer username from claims during an argocd relogin (resolves #475) (#483) 2018-07-31 10:05:52 -07:00
Jesse Suen
231d86e249 Create update-manifests.sh script to support manifest generation for personal images (#477)
Tweaks to README and getting_started.md
2018-07-30 16:13:54 -07:00
Jesse Suen
5fb8b3f73c Update getting_started.md with relogin command during password change (#473) 2018-07-27 18:35:52 -07:00
34 changed files with 1824 additions and 469 deletions

View File

@@ -1,5 +1,22 @@
# Changelog
## v0.7.2 (2018-08-21)
- API discovery becomes best effort when partial resource list is returned (issue #524)
## v0.7.1 (2018-08-03)
+ Surface helm parameters to the application level (#485)
+ [UI] Improve application creation wizard (#459)
+ [UI] Show indicator when refresh is still in progress (#493)
* [UI] Improve data loading error notification (#446)
* Infer username from claims during an `argocd relogin` (#475)
* Expand RBAC role to be able to create application events. Fix username claims extraction
- Fix scalability issues with the ListApps API (#494)
- Fix issue where application server was retrieving events from incorrect cluster (#478)
- Fix failure in identifying app source type when path was '.'
- AppProjectSpec SourceRepos mislabeled (#490)
- Failed e2e test was not failing CI workflow
* Fix linux download link in getting_started.md (#487) (@chocopowwwa)
## v0.7.0 (2018-07-27)
+ Support helm charts and yaml directories as an application source
+ Audit trails in the form of API call logs

View File

@@ -38,6 +38,18 @@ NOTE: The make command can take a while, and we recommend building the specific
* `make codegen` - Builds protobuf and swagger files
* `make argocd-util` - Make the administrator's utility, used for certain tasks such as import/export
## Generating ArgoCD manifests for a specific image repository/tag
During development, the `update-manifests.sh` script, can be used to conveniently regenerate the
ArgoCD installation manifests with a customized image namespace and tag. This enables developers
to easily apply manifests which are using the images that they pushed into their personal container
repository.
```
$ IMAGE_NAMESPACE=jessesuen IMAGE_TAG=latest ./hack/update-manifests.sh
$ kubectl apply -n argocd -f ./manifests/install.yaml
```
## Running locally
You need to have access to kubernetes cluster (including [minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) or [docker edge](https://docs.docker.com/docker-for-mac/install/) ) in order to run Argo CD on your laptop:

370
Gopkg.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -81,8 +81,7 @@ argocd-util: clean-debug
.PHONY: install-manifest
install-manifest:
if [ "${IMAGE_NAMESPACE}" = "" ] ; then echo "IMAGE_NAMESPACE must be set to build install manifest" ; exit 1 ; fi
echo "# This is an auto-generated file. DO NOT EDIT" > manifests/install.yaml
cat manifests/components/*.yaml | sed 's@\( image: argoproj/\(.*\):latest\)@ image: '"${IMAGE_NAMESPACE}"'/\2:'"${IMAGE_TAG}"'@g' >> manifests/install.yaml
./hack/update-manifests.sh
.PHONY: server
server: clean-debug

View File

@@ -3,7 +3,7 @@
## What is Argo CD?
Argo CD is a declarative, continuous delivery for Kubernetes.
Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes.
![Argo CD UI](docs/argocd-ui.gif)
@@ -55,13 +55,15 @@ For additional details, see [architecture overview](docs/architecture.md).
* SSO Integration (OIDC, OAuth2, LDAP, SAML 2.0, GitLab, Microsoft, LinkedIn)
* Webhook Integration (GitHub, BitBucket, GitLab)
* PreSync, Sync, PostSync hooks to support complex application rollouts (e.g.blue/green & canary upgrades)
* Audit trails for application events and API calls
* Parameter overrides for overriding ksonnet/helm parameters in git
## Development Status
* Argo CD is being used in production to deploy SaaS services at Intuit
## Roadmap
* Audit trails for application events and API calls
* Auto-sync toggle to directly apply git state changes to live state
* Service account/access key management for CI pipelines
* Revamped UI
* Support for additional config management tools (Kustomize?)
* Revamped UI, and feature parity with CLI
* Customizable application actions

View File

@@ -1 +1 @@
0.7.0
0.7.2

View File

@@ -17,7 +17,6 @@ import (
// NewReloginCommand returns a new instance of `argocd relogin` command
func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Command {
var (
username string
password string
)
var command = &cobra.Command{
@@ -54,8 +53,10 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
PlainText: configCtx.Server.PlainText,
}
acdClient := argocdclient.NewClientOrDie(&clientOpts)
tokenString = passwordLogin(acdClient, username, password)
fmt.Printf("Relogging in as '%s'\n", claims.Subject)
tokenString = passwordLogin(acdClient, claims.Subject, password)
} else {
fmt.Println("Reinitiating SSO login")
tokenString, refreshToken = oauth2Login(configCtx.Server.Server, configCtx.Server.PlainText)
}
@@ -69,7 +70,6 @@ func NewReloginCommand(globalClientOpts *argocdclient.ClientOptions) *cobra.Comm
fmt.Printf("Context '%s' updated\n", localCfg.CurrentContext)
},
}
command.Flags().StringVar(&username, "username", "", "the username of an account to authenticate")
command.Flags().StringVar(&password, "password", "", "the password of an account to authenticate")
return command
}

View File

@@ -90,7 +90,7 @@ func NewApplicationController(
statusRefreshTimeout: appResyncPeriod,
forceRefreshApps: make(map[string]bool),
forceRefreshAppsMutex: &sync.Mutex{},
auditLogger: argo.NewAuditLogger(namespace, kubeClientset, "appcontroller"),
auditLogger: argo.NewAuditLogger(namespace, kubeClientset, "application-controller"),
}
}
@@ -204,7 +204,7 @@ func retryUntilSucceed(action func() error, desc string, ctx context.Context, ti
log.Infof("Stop retrying %s", desc)
return
} else {
log.Warnf("Failed to %s: %v, retrying in %v", desc, err, timeout)
log.Warnf("Failed to %s: %+v, retrying in %v", desc, err, timeout)
time.Sleep(timeout)
}
}

View File

@@ -9,21 +9,29 @@ An example guestbook application is provided to demonstrate how ArgoCD works.
## 1. Install ArgoCD
```
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/master/manifests/install.yaml
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v0.7.1/manifests/install.yaml
```
This will create a new namespace, `argocd`, where ArgoCD services and application resources will live.
NOTE:
* On GKE with RBAC enabled, you may need to grant your account the ability to create new cluster roles
```
$ kubectl create clusterrolebinding YOURNAME-cluster-admin-binding --clusterrole=cluster-admin --user=YOUREMAIL@gmail.com
kubectl create clusterrolebinding YOURNAME-cluster-admin-binding --clusterrole=cluster-admin --user=YOUREMAIL@gmail.com
```
## 2. Download ArgoCD CLI
Download the latest ArgoCD version:
On Mac:
```
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v0.7.0/argocd-darwin-amd64
brew install argoproj/tap/argocd
```
On Linux:
```
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/download/v0.7.1/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
```
@@ -59,6 +67,7 @@ argocd login <EXTERNAL-IP>
After logging in, change the password using the command:
```
argocd account update-password
argocd relogin
```

11
hack/update-manifests.sh Executable file
View File

@@ -0,0 +1,11 @@
#!/bin/sh
IMAGE_NAMESPACE=${IMAGE_NAMESPACE:='argoproj'}
IMAGE_TAG=${IMAGE_TAG:='latest'}
for i in "$(ls manifests/components/*.yaml)"; do
sed -i '' 's@\( image: \(.*\)/\(argocd-.*\):.*\)@ image: '"${IMAGE_NAMESPACE}"'/\3:'"${IMAGE_TAG}"'@g' $i
done
echo "# This is an auto-generated file. DO NOT EDIT" > manifests/install.yaml
cat manifests/components/*.yaml >> manifests/install.yaml

View File

@@ -27,3 +27,11 @@ rules:
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- list

View File

@@ -14,6 +14,6 @@ spec:
spec:
containers:
- command: [/argocd-application-controller, --repo-server, 'argocd-repo-server:8081']
image: argoproj/argocd-application-controller:latest
image: argoproj/argocd-application-controller:v0.7.2
name: application-controller
serviceAccountName: application-controller

View File

@@ -30,3 +30,10 @@ rules:
- update
- delete
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- list

View File

@@ -15,20 +15,20 @@ spec:
serviceAccountName: argocd-server
initContainers:
- name: copyutil
image: argoproj/argocd-server:latest
image: argoproj/argocd-server:v0.7.2
command: [cp, /argocd-util, /shared]
volumeMounts:
- mountPath: /shared
name: static-files
- name: ui
image: argoproj/argocd-ui:latest
image: argoproj/argocd-ui:v0.7.2
command: [cp, -r, /app, /shared]
volumeMounts:
- mountPath: /shared
name: static-files
containers:
- name: argocd-server
image: argoproj/argocd-server:latest
image: argoproj/argocd-server:v0.7.2
command: [/argocd-server, --staticassets, /shared/app, --repo-server, 'argocd-repo-server:8081']
volumeMounts:
- mountPath: /shared

View File

@@ -14,7 +14,7 @@ spec:
spec:
containers:
- name: argocd-repo-server
image: argoproj/argocd-repo-server:latest
image: argoproj/argocd-repo-server:v0.7.2
command: [/argocd-repo-server]
ports:
- containerPort: 8081

View File

@@ -126,6 +126,14 @@ rules:
- update
- patch
- delete
- apiGroups:
- ""
resources:
- events
verbs:
- create
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
@@ -154,7 +162,7 @@ spec:
spec:
containers:
- command: [/argocd-application-controller, --repo-server, 'argocd-repo-server:8081']
image: argoproj/argocd-application-controller:v0.7.0
image: argoproj/argocd-application-controller:v0.7.2
name: application-controller
serviceAccountName: application-controller
---
@@ -194,6 +202,13 @@ rules:
- update
- delete
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- list
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
@@ -223,20 +238,20 @@ spec:
serviceAccountName: argocd-server
initContainers:
- name: copyutil
image: argoproj/argocd-server:v0.7.0
image: argoproj/argocd-server:v0.7.2
command: [cp, /argocd-util, /shared]
volumeMounts:
- mountPath: /shared
name: static-files
- name: ui
image: argoproj/argocd-ui:v0.7.0
image: argoproj/argocd-ui:v0.7.2
command: [cp, -r, /app, /shared]
volumeMounts:
- mountPath: /shared
name: static-files
containers:
- name: argocd-server
image: argoproj/argocd-server:v0.7.0
image: argoproj/argocd-server:v0.7.2
command: [/argocd-server, --staticassets, /shared/app, --repo-server, 'argocd-repo-server:8081']
volumeMounts:
- mountPath: /shared
@@ -283,7 +298,7 @@ spec:
spec:
containers:
- name: argocd-repo-server
image: argoproj/argocd-repo-server:v0.7.0
image: argoproj/argocd-repo-server:v0.7.2
command: [/argocd-repo-server]
ports:
- containerPort: 8081

View File

@@ -7781,153 +7781,153 @@ var fileDescriptorGenerated = []byte{
0x19, 0x76, 0xcf, 0x6b, 0x67, 0xfe, 0xd9, 0x87, 0x5d, 0x79, 0x30, 0x38, 0xd2, 0xee, 0xaa, 0xc3,
0xc3, 0xa0, 0x64, 0x06, 0x1b, 0x02, 0xe6, 0x21, 0x24, 0xcf, 0xae, 0x1d, 0x6f, 0xd6, 0x8f, 0xa5,
0x66, 0x13, 0xa4, 0x10, 0x05, 0xda, 0x3d, 0xb5, 0x33, 0xed, 0x99, 0xe9, 0xee, 0x74, 0xd5, 0x8c,
0x35, 0x12, 0x41, 0x46, 0x08, 0x89, 0xa7, 0x04, 0x42, 0x88, 0x2b, 0x07, 0x4e, 0x08, 0x09, 0x09,
0x71, 0x42, 0xe2, 0x00, 0x07, 0xe4, 0x63, 0x0e, 0x20, 0xa2, 0x80, 0x56, 0x78, 0x73, 0x89, 0xc4,
0x81, 0x7b, 0x4e, 0xa8, 0x1e, 0xdd, 0x55, 0xdd, 0xb3, 0xcb, 0xae, 0x3d, 0x6d, 0x03, 0xb7, 0xee,
0xff, 0xff, 0xfb, 0xff, 0xfe, 0xfa, 0xeb, 0xaf, 0xff, 0x51, 0x0d, 0x5b, 0x3d, 0x8f, 0xf5, 0xc7,
0xb7, 0x9a, 0x6e, 0x30, 0x6a, 0x39, 0x51, 0x2f, 0x08, 0xa3, 0xe0, 0xb6, 0x78, 0x78, 0xde, 0xed,
0xb6, 0xc2, 0x41, 0xaf, 0xe5, 0x84, 0x1e, 0x6d, 0x39, 0x61, 0x38, 0xf4, 0x5c, 0x87, 0x79, 0x81,
0xdf, 0x9a, 0x9c, 0x77, 0x86, 0x61, 0xdf, 0x39, 0xdf, 0xea, 0x11, 0x9f, 0x44, 0x0e, 0x23, 0xdd,
0x66, 0x18, 0x05, 0x2c, 0x40, 0x9f, 0xd5, 0xaa, 0x9a, 0xb1, 0x2a, 0xf1, 0xf0, 0x55, 0xb7, 0xdb,
0x0c, 0x07, 0xbd, 0x26, 0x57, 0xd5, 0x34, 0x54, 0x35, 0x63, 0x55, 0x67, 0x9f, 0x37, 0xac, 0xe8,
0x05, 0xbd, 0xa0, 0x25, 0x34, 0xde, 0x1a, 0xef, 0x89, 0x37, 0xf1, 0x22, 0x9e, 0x24, 0xd2, 0xd9,
0x4f, 0x0d, 0x2e, 0xd2, 0xa6, 0x17, 0x70, 0xdb, 0x46, 0x8e, 0xdb, 0xf7, 0x7c, 0x12, 0x4d, 0xb5,
0xb1, 0x23, 0xc2, 0x9c, 0xd6, 0x64, 0xc6, 0xbe, 0xb3, 0xad, 0xa3, 0xbe, 0x8a, 0xc6, 0x3e, 0xf3,
0x46, 0x64, 0xe6, 0x83, 0x4f, 0x1f, 0xf7, 0x01, 0x75, 0xfb, 0x64, 0xe4, 0xcc, 0x7c, 0xf7, 0xc9,
0xa3, 0xbe, 0x1b, 0x33, 0x6f, 0xd8, 0xf2, 0x7c, 0x46, 0x59, 0x94, 0xfd, 0xc8, 0xfe, 0x9b, 0x05,
0x70, 0x29, 0x0c, 0x77, 0xa2, 0xe0, 0x36, 0x71, 0x19, 0xfa, 0x1a, 0x54, 0xf9, 0x3a, 0xba, 0x0e,
0x73, 0x1a, 0xd6, 0xba, 0x75, 0xae, 0x7e, 0xe1, 0x13, 0x4d, 0xa9, 0xb6, 0x69, 0xaa, 0xd5, 0x7e,
0xe5, 0xd2, 0xcd, 0xc9, 0xf9, 0xe6, 0xcd, 0x5b, 0xfc, 0xfb, 0xeb, 0x84, 0x39, 0x6d, 0x74, 0x6f,
0x7f, 0xed, 0xd4, 0xc1, 0xfe, 0x1a, 0x68, 0x1a, 0x4e, 0xb4, 0xa2, 0x01, 0x94, 0x68, 0x48, 0xdc,
0x46, 0x41, 0x68, 0xdf, 0x6a, 0x3e, 0xf4, 0xee, 0x35, 0xb5, 0xd9, 0x9d, 0x90, 0xb8, 0xed, 0x45,
0x05, 0x5b, 0xe2, 0x6f, 0x58, 0x80, 0xd8, 0xef, 0x58, 0xb0, 0xac, 0xc5, 0xae, 0x79, 0x94, 0xa1,
0xd7, 0x66, 0x56, 0xd8, 0x3c, 0xd9, 0x0a, 0xf9, 0xd7, 0x62, 0x7d, 0xa7, 0x15, 0x50, 0x35, 0xa6,
0x18, 0xab, 0xbb, 0x0d, 0x65, 0x8f, 0x91, 0x11, 0x6d, 0x14, 0xd6, 0x8b, 0xe7, 0xea, 0x17, 0x2e,
0xe7, 0xb2, 0xbc, 0xf6, 0x92, 0x42, 0x2c, 0x6f, 0x71, 0xdd, 0x58, 0x42, 0xd8, 0x77, 0x0b, 0xe6,
0xe2, 0xf8, 0xaa, 0xd1, 0xc7, 0x60, 0x81, 0x06, 0xe3, 0xc8, 0x25, 0xb4, 0x61, 0xad, 0x17, 0xcf,
0xd5, 0xda, 0x2b, 0x07, 0xfb, 0x6b, 0xf5, 0x8e, 0x20, 0x61, 0x12, 0x06, 0x14, 0xc7, 0x7c, 0xf4,
0x7d, 0x0b, 0x16, 0xbb, 0x84, 0x32, 0xcf, 0x17, 0xb8, 0xb1, 0xc5, 0x5f, 0x9a, 0xcf, 0xe2, 0x98,
0xb8, 0xa9, 0x35, 0xb7, 0x9f, 0x54, 0xd6, 0x2f, 0x1a, 0x44, 0x8a, 0x53, 0xe0, 0xe8, 0x05, 0xa8,
0x77, 0x09, 0x75, 0x23, 0x2f, 0xe4, 0xef, 0x8d, 0xe2, 0xba, 0x75, 0xae, 0xd6, 0x7e, 0x42, 0x7d,
0x58, 0xdf, 0xd4, 0x2c, 0x6c, 0xca, 0xd9, 0x7f, 0x2a, 0x42, 0xdd, 0x40, 0x7d, 0x0c, 0xe1, 0x3b,
0x4c, 0x85, 0xef, 0x4b, 0xf9, 0x78, 0xeb, 0xa8, 0xf8, 0x45, 0x0c, 0x2a, 0x94, 0x39, 0x6c, 0x4c,
0x85, 0x47, 0xea, 0x17, 0xae, 0xe5, 0x84, 0x27, 0x74, 0xb6, 0x97, 0x15, 0x62, 0x45, 0xbe, 0x63,
0x85, 0x85, 0xde, 0x80, 0x5a, 0x10, 0xf2, 0x2c, 0xc1, 0xb7, 0xa2, 0x24, 0x80, 0x37, 0xe7, 0x00,
0xbe, 0x19, 0xeb, 0x6a, 0x2f, 0x1d, 0xec, 0xaf, 0xd5, 0x92, 0x57, 0xac, 0x51, 0x6c, 0x17, 0x9e,
0x34, 0xec, 0xdb, 0x08, 0xfc, 0xae, 0x27, 0x36, 0x74, 0x1d, 0x4a, 0x6c, 0x1a, 0x12, 0xb1, 0x99,
0x35, 0xed, 0xa2, 0xdd, 0x69, 0x48, 0xb0, 0xe0, 0xf0, 0x90, 0x1f, 0x11, 0x4a, 0x9d, 0x1e, 0x11,
0x7b, 0x52, 0x6b, 0xaf, 0x28, 0xa1, 0x85, 0xeb, 0x92, 0x8c, 0x63, 0xbe, 0xfd, 0x06, 0x3c, 0x7d,
0x78, 0x88, 0xa2, 0x8f, 0x40, 0x85, 0x92, 0x68, 0x42, 0x22, 0x05, 0xa4, 0x3d, 0x23, 0xa8, 0x58,
0x71, 0x51, 0x0b, 0x6a, 0xbe, 0x33, 0x22, 0x34, 0x74, 0xdc, 0x18, 0xee, 0x8c, 0x12, 0xad, 0xdd,
0x88, 0x19, 0x58, 0xcb, 0xd8, 0x7f, 0xb7, 0x60, 0xc5, 0xc0, 0x7c, 0x0c, 0x19, 0x68, 0x90, 0xce,
0x40, 0x57, 0xf2, 0x89, 0x98, 0x23, 0x52, 0xd0, 0x1f, 0x8a, 0x70, 0xc6, 0x8c, 0x2b, 0x91, 0x5b,
0xf8, 0x96, 0x44, 0x24, 0x0c, 0x5e, 0xc6, 0xd7, 0x94, 0x3b, 0x93, 0x2d, 0xc1, 0x92, 0x8c, 0x63,
0x3e, 0xdf, 0xdf, 0xd0, 0x61, 0x7d, 0xe5, 0xcb, 0x64, 0x7f, 0x77, 0x1c, 0xd6, 0xc7, 0x82, 0xc3,
0x33, 0x03, 0xf1, 0x27, 0x5e, 0x14, 0xf8, 0x23, 0xe2, 0xb3, 0x6c, 0x66, 0xb8, 0xac, 0x59, 0xd8,
0x94, 0x43, 0x5f, 0x84, 0x65, 0xe6, 0x44, 0x3d, 0xc2, 0x30, 0x99, 0x78, 0x34, 0x0e, 0xe4, 0x5a,
0xfb, 0x69, 0xf5, 0xe5, 0xf2, 0x6e, 0x8a, 0x8b, 0x33, 0xd2, 0xe8, 0xb7, 0x16, 0x3c, 0xe3, 0x06,
0xa3, 0x30, 0xf0, 0x89, 0xcf, 0x76, 0x9c, 0xc8, 0x19, 0x11, 0x46, 0xa2, 0x9b, 0x13, 0x12, 0x45,
0x5e, 0x97, 0xd0, 0x46, 0x59, 0x78, 0xf7, 0xfa, 0x1c, 0xde, 0xdd, 0x98, 0xd1, 0xde, 0x7e, 0x56,
0x19, 0xf7, 0xcc, 0xc6, 0xd1, 0xc8, 0xf8, 0x3f, 0x99, 0x85, 0xce, 0x43, 0x7d, 0xe2, 0x0c, 0xc7,
0x84, 0x5e, 0xf1, 0x86, 0x84, 0x36, 0x2a, 0xba, 0x08, 0xbc, 0xa2, 0xc9, 0xd8, 0x94, 0xb1, 0x7f,
0x5f, 0x48, 0x85, 0x68, 0x27, 0xce, 0x3b, 0x62, 0x2f, 0x55, 0x80, 0xe6, 0x95, 0x77, 0x84, 0x4e,
0xe3, 0x74, 0xc9, 0xc2, 0xa4, 0xb0, 0xd0, 0x77, 0x2c, 0x51, 0x05, 0xe2, 0x53, 0xa9, 0x72, 0xec,
0x23, 0xa8, 0x48, 0x66, 0x61, 0x89, 0x89, 0xd8, 0x84, 0xe6, 0x21, 0x1c, 0xca, 0xba, 0xaa, 0x22,
0x2e, 0x09, 0x61, 0x55, 0x6e, 0x71, 0xcc, 0xb7, 0x7f, 0x5e, 0x49, 0x9f, 0x01, 0x99, 0x43, 0x7f,
0x6c, 0xc1, 0x69, 0xbe, 0x51, 0x4e, 0xe4, 0xd1, 0xc0, 0xc7, 0x84, 0x8e, 0x87, 0x4c, 0x39, 0x73,
0x7b, 0xce, 0xa0, 0x31, 0x55, 0xb6, 0x1b, 0xca, 0xae, 0xd3, 0x59, 0x0e, 0x9e, 0x81, 0x47, 0x0c,
0x16, 0xfa, 0x1e, 0x65, 0x41, 0x34, 0x55, 0xc9, 0x61, 0x9e, 0xee, 0x6b, 0x93, 0x84, 0xc3, 0x60,
0xca, 0xcf, 0xda, 0x96, 0xbf, 0x17, 0x68, 0xff, 0x5c, 0x95, 0x08, 0x38, 0x86, 0x42, 0xdf, 0xb4,
0x00, 0xc2, 0x38, 0x52, 0x79, 0x21, 0x7b, 0x04, 0x07, 0x27, 0xa9, 0xd9, 0x09, 0x89, 0x62, 0x03,
0x14, 0x05, 0x50, 0xe9, 0x13, 0x67, 0xc8, 0xfa, 0xaa, 0x9c, 0xbd, 0x38, 0x07, 0xfc, 0x55, 0xa1,
0x28, 0x5b, 0x42, 0x25, 0x15, 0x2b, 0x18, 0xf4, 0x6d, 0x0b, 0x96, 0x93, 0xea, 0xc6, 0x65, 0x49,
0xa3, 0x3c, 0x77, 0xc3, 0x7b, 0x33, 0xa5, 0xb0, 0x8d, 0x78, 0x1a, 0x4b, 0xd3, 0x70, 0x06, 0x14,
0x7d, 0xcb, 0x02, 0x70, 0xe3, 0x6a, 0x2a, 0xf3, 0x41, 0xfd, 0xc2, 0xcd, 0x7c, 0x4e, 0x54, 0x52,
0xa5, 0xb5, 0xfb, 0x13, 0x12, 0xc5, 0x06, 0xac, 0xfd, 0xae, 0x05, 0x4f, 0x19, 0x1f, 0x7e, 0xd9,
0x61, 0x6e, 0xff, 0xf2, 0x84, 0xa7, 0xe9, 0xed, 0x54, 0x7d, 0xff, 0x8c, 0x59, 0xdf, 0xdf, 0xdf,
0x5f, 0xfb, 0xe8, 0x51, 0x13, 0xcd, 0x1d, 0xae, 0xa1, 0x29, 0x54, 0x18, 0xad, 0xc0, 0x9b, 0x50,
0x37, 0x6c, 0x56, 0xe9, 0x23, 0xaf, 0x02, 0x98, 0xe4, 0x0c, 0x83, 0x88, 0x4d, 0x3c, 0xfb, 0x2f,
0x05, 0x58, 0xd8, 0x18, 0x8e, 0x29, 0x23, 0xd1, 0x89, 0x1b, 0x8a, 0x75, 0x28, 0xf1, 0x66, 0x21,
0x5b, 0xff, 0x78, 0x2f, 0x81, 0x05, 0x07, 0x85, 0x50, 0x71, 0x03, 0x7f, 0xcf, 0xeb, 0xa9, 0x16,
0xf0, 0xea, 0x3c, 0x27, 0x47, 0x5a, 0xb7, 0x21, 0xf4, 0x69, 0x9b, 0xe4, 0x3b, 0x56, 0x38, 0xe8,
0x87, 0x16, 0xac, 0xb8, 0x81, 0xef, 0x13, 0x57, 0x07, 0x6f, 0x69, 0xee, 0x76, 0x77, 0x23, 0xad,
0xb1, 0xfd, 0x01, 0x85, 0xbe, 0x92, 0x61, 0xe0, 0x2c, 0xb6, 0xfd, 0x9b, 0x02, 0x2c, 0xa5, 0x2c,
0x47, 0xcf, 0x41, 0x75, 0x4c, 0x49, 0x24, 0x3c, 0x27, 0xfd, 0x9b, 0x74, 0x44, 0x2f, 0x2b, 0x3a,
0x4e, 0x24, 0xb8, 0x74, 0xe8, 0x50, 0x7a, 0x27, 0x88, 0xba, 0xca, 0xcf, 0x89, 0xf4, 0x8e, 0xa2,
0xe3, 0x44, 0x82, 0xf7, 0x1b, 0xb7, 0x88, 0x13, 0x91, 0x68, 0x37, 0x18, 0x90, 0x99, 0x49, 0xa4,
0xad, 0x59, 0xd8, 0x94, 0x13, 0x4e, 0x63, 0x43, 0xba, 0x31, 0xf4, 0x88, 0xcf, 0xa4, 0x99, 0x39,
0x38, 0x6d, 0xf7, 0x5a, 0xc7, 0xd4, 0xa8, 0x9d, 0x96, 0x61, 0xe0, 0x2c, 0xb6, 0xfd, 0x67, 0x0b,
0xea, 0xca, 0x69, 0x8f, 0xa1, 0xe9, 0xec, 0xa5, 0x9b, 0xce, 0xf6, 0xfc, 0x31, 0x7a, 0x44, 0xc3,
0xf9, 0xab, 0x22, 0xcc, 0x54, 0x3a, 0xf4, 0x3a, 0xcf, 0x71, 0x9c, 0x46, 0xba, 0x97, 0xe2, 0x22,
0xfb, 0xf1, 0x93, 0xad, 0x6e, 0xd7, 0x1b, 0x11, 0x33, 0x7d, 0xc5, 0x5a, 0xb0, 0xa1, 0x11, 0xdd,
0xb5, 0x34, 0xc0, 0x6e, 0xa0, 0xf2, 0x4a, 0xbe, 0x2d, 0xd1, 0x8c, 0x09, 0xbb, 0x01, 0x36, 0x30,
0xd1, 0xe7, 0x92, 0x41, 0xb0, 0x2c, 0x02, 0xd2, 0x4e, 0x8f, 0x6e, 0xef, 0xa7, 0x1a, 0x80, 0xcc,
0x38, 0x37, 0x85, 0x5a, 0x44, 0xe2, 0x6b, 0x01, 0x59, 0x01, 0xe6, 0x49, 0x22, 0x58, 0xe9, 0x92,
0xc7, 0x38, 0x19, 0x7f, 0x62, 0x32, 0xc5, 0x1a, 0xcd, 0xfe, 0x81, 0x05, 0x68, 0xb6, 0x5c, 0xf3,
0x31, 0x2a, 0x69, 0x62, 0xd5, 0x01, 0x4e, 0xf4, 0x24, 0xe2, 0x58, 0xcb, 0x9c, 0x20, 0x4d, 0x3e,
0x0b, 0x65, 0xd1, 0xd4, 0xaa, 0x03, 0x9b, 0x44, 0x8f, 0x68, 0x7b, 0xb1, 0xe4, 0xd9, 0x7f, 0xb4,
0x20, 0x9b, 0x6e, 0x44, 0xa6, 0x96, 0x9e, 0xcd, 0x66, 0xea, 0xb4, 0x17, 0x4f, 0x3e, 0x67, 0xa2,
0xd7, 0xa0, 0xee, 0x30, 0x46, 0x46, 0x21, 0x13, 0x01, 0x59, 0x7c, 0xe0, 0x80, 0x5c, 0xe6, 0x91,
0x70, 0x3d, 0xe8, 0x7a, 0x7b, 0x9e, 0x08, 0x46, 0x53, 0x9d, 0xfd, 0x5e, 0x11, 0x96, 0xd3, 0xcd,
0x17, 0x1a, 0x43, 0x45, 0x34, 0x3b, 0xf2, 0xd6, 0x27, 0xf7, 0xee, 0x2a, 0x71, 0x89, 0x20, 0x51,
0xac, 0xc0, 0x78, 0x62, 0x8d, 0xe2, 0xe9, 0x2a, 0x93, 0x58, 0x93, 0xb9, 0x2a, 0x91, 0x38, 0x76,
0xa2, 0x2a, 0xfe, 0x6f, 0x4e, 0x54, 0xaf, 0x03, 0x74, 0x85, 0xb7, 0xc5, 0x5e, 0x96, 0x1e, 0x3e,
0xb9, 0x6c, 0x26, 0x5a, 0xb0, 0xa1, 0x11, 0x9d, 0x85, 0x82, 0xd7, 0x15, 0xa7, 0xba, 0xd8, 0x06,
0x25, 0x5b, 0xd8, 0xda, 0xc4, 0x05, 0xaf, 0x6b, 0x53, 0x58, 0x34, 0xbb, 0xcd, 0x13, 0xc7, 0xea,
0xe7, 0x61, 0x49, 0x3e, 0x6d, 0x12, 0xe6, 0x78, 0x43, 0xaa, 0x76, 0xe7, 0x29, 0x25, 0xbe, 0xd4,
0x31, 0x99, 0x38, 0x2d, 0x6b, 0xff, 0xac, 0x00, 0x70, 0x35, 0x08, 0x06, 0x0a, 0x33, 0x3e, 0x7a,
0xd6, 0x91, 0x47, 0x6f, 0x1d, 0x4a, 0x03, 0xcf, 0xef, 0x66, 0x0f, 0xe7, 0xb6, 0xe7, 0x77, 0xb1,
0xe0, 0xa0, 0x0b, 0x00, 0x4e, 0xe8, 0xbd, 0x42, 0x22, 0xaa, 0x2f, 0xf7, 0x12, 0xbf, 0x5c, 0xda,
0xd9, 0x52, 0x1c, 0x6c, 0x48, 0xa1, 0xe7, 0x54, 0x67, 0x28, 0xc7, 0xf6, 0x46, 0xa6, 0x33, 0xac,
0x72, 0x0b, 0x8d, 0xd6, 0xef, 0x62, 0x26, 0x3f, 0xae, 0xcf, 0xe4, 0x47, 0xdd, 0x29, 0xef, 0xf4,
0x1d, 0x4a, 0x0e, 0x3b, 0xd7, 0x95, 0x63, 0xee, 0x8f, 0xfe, 0x69, 0x81, 0xbe, 0xbd, 0x42, 0x7b,
0x50, 0xa2, 0x53, 0xdf, 0x55, 0xf5, 0x66, 0x9e, 0x8c, 0xda, 0x99, 0xfa, 0xae, 0xbe, 0x24, 0xab,
0x8a, 0x3b, 0xc0, 0xa9, 0xef, 0x62, 0xa1, 0x1f, 0x4d, 0xa0, 0x1a, 0x05, 0xc3, 0xe1, 0x2d, 0xc7,
0x1d, 0xe4, 0x50, 0x7a, 0xb0, 0x52, 0xa5, 0xf1, 0x16, 0xc5, 0x79, 0x55, 0x64, 0x9c, 0x60, 0xd9,
0xbf, 0x2e, 0x43, 0x66, 0xba, 0x40, 0x63, 0xf3, 0x62, 0xd0, 0xca, 0xf1, 0x62, 0x30, 0xc9, 0xfe,
0x87, 0x5d, 0x0e, 0xa2, 0x17, 0xa0, 0x1c, 0xf2, 0x3d, 0x53, 0x11, 0xb6, 0x16, 0xe7, 0x76, 0xb1,
0x91, 0x87, 0x6c, 0xad, 0x94, 0x36, 0x77, 0xb6, 0x78, 0x4c, 0xc6, 0xfe, 0x06, 0x00, 0xf7, 0xb5,
0x1a, 0xd3, 0xe5, 0x21, 0xbf, 0x91, 0xd7, 0x8e, 0xaa, 0x49, 0x5d, 0x24, 0xf5, 0x4e, 0x82, 0x82,
0x0d, 0x44, 0xf4, 0x3d, 0x0b, 0x96, 0x63, 0xc7, 0x2b, 0x23, 0xca, 0x8f, 0xc4, 0x08, 0x31, 0x33,
0xe2, 0x14, 0x12, 0xce, 0x20, 0xa3, 0xaf, 0x40, 0x8d, 0x32, 0x27, 0x92, 0xc5, 0xab, 0xf2, 0xc0,
0x09, 0x2f, 0xd9, 0xcb, 0x4e, 0xac, 0x04, 0x6b, 0x7d, 0xe8, 0x55, 0x80, 0x3d, 0xcf, 0xf7, 0x68,
0x5f, 0x68, 0x5f, 0x78, 0xb8, 0xd2, 0x78, 0x25, 0xd1, 0x80, 0x0d, 0x6d, 0xf6, 0x5f, 0x0b, 0x00,
0xe2, 0x2f, 0x87, 0x27, 0x2e, 0x1e, 0xd6, 0xa1, 0x14, 0x91, 0x30, 0xc8, 0x66, 0x2e, 0x2e, 0x81,
0x05, 0x27, 0x35, 0x47, 0x14, 0x1e, 0x68, 0x8e, 0x28, 0x1e, 0x3b, 0x47, 0xf0, 0x1c, 0x4c, 0xfb,
0x3b, 0x91, 0x37, 0x71, 0x18, 0xd9, 0x26, 0x53, 0x95, 0xc8, 0x74, 0x0e, 0xee, 0x5c, 0xd5, 0x4c,
0x9c, 0x96, 0x3d, 0x74, 0x04, 0x2b, 0xff, 0x17, 0x47, 0xb0, 0x77, 0x2c, 0x58, 0xd6, 0x9e, 0xfd,
0xff, 0xfa, 0x8f, 0xa6, 0xed, 0x3e, 0x62, 0xa6, 0xf8, 0x97, 0x05, 0x2b, 0x71, 0xf7, 0xaa, 0x8a,
0x35, 0x12, 0x41, 0x41, 0x08, 0x29, 0xbc, 0x24, 0x10, 0x42, 0x5c, 0x39, 0x70, 0x42, 0x48, 0x48,
0x88, 0x13, 0x12, 0x07, 0x38, 0x20, 0x1f, 0x73, 0x00, 0x11, 0x05, 0xb4, 0xc2, 0x9b, 0x4b, 0x24,
0x0e, 0xdc, 0x73, 0x42, 0xf5, 0xe8, 0xae, 0xea, 0x9e, 0x5d, 0x76, 0xed, 0x69, 0x1b, 0x72, 0xeb,
0xfe, 0xff, 0xbf, 0xff, 0xef, 0xaf, 0xbf, 0xfe, 0xfa, 0x1f, 0xd5, 0xb0, 0xd5, 0xf3, 0x58, 0x7f,
0x7c, 0xab, 0xe9, 0x06, 0xa3, 0x96, 0x13, 0xf5, 0x82, 0x30, 0x0a, 0x6e, 0x8b, 0x87, 0x67, 0xdd,
0x6e, 0x2b, 0x1c, 0xf4, 0x5a, 0x4e, 0xe8, 0xd1, 0x96, 0x13, 0x86, 0x43, 0xcf, 0x75, 0x98, 0x17,
0xf8, 0xad, 0xc9, 0x79, 0x67, 0x18, 0xf6, 0x9d, 0xf3, 0xad, 0x1e, 0xf1, 0x49, 0xe4, 0x30, 0xd2,
0x6d, 0x86, 0x51, 0xc0, 0x02, 0xf4, 0x79, 0xad, 0xaa, 0x19, 0xab, 0x12, 0x0f, 0x5f, 0x77, 0xbb,
0xcd, 0x70, 0xd0, 0x6b, 0x72, 0x55, 0x4d, 0x43, 0x55, 0x33, 0x56, 0x75, 0xf6, 0x59, 0xc3, 0x8a,
0x5e, 0xd0, 0x0b, 0x5a, 0x42, 0xe3, 0xad, 0xf1, 0x9e, 0x78, 0x13, 0x2f, 0xe2, 0x49, 0x22, 0x9d,
0xfd, 0xcc, 0xe0, 0x22, 0x6d, 0x7a, 0x01, 0xb7, 0x6d, 0xe4, 0xb8, 0x7d, 0xcf, 0x27, 0xd1, 0x54,
0x1b, 0x3b, 0x22, 0xcc, 0x69, 0x4d, 0x66, 0xec, 0x3b, 0xdb, 0x3a, 0xea, 0xab, 0x68, 0xec, 0x33,
0x6f, 0x44, 0x66, 0x3e, 0xf8, 0xec, 0x71, 0x1f, 0x50, 0xb7, 0x4f, 0x46, 0xce, 0xcc, 0x77, 0x9f,
0x3e, 0xea, 0xbb, 0x31, 0xf3, 0x86, 0x2d, 0xcf, 0x67, 0x94, 0x45, 0xd9, 0x8f, 0xec, 0xbf, 0x5b,
0x00, 0x97, 0xc2, 0x70, 0x27, 0x0a, 0x6e, 0x13, 0x97, 0xa1, 0x6f, 0x40, 0x95, 0xaf, 0xa3, 0xeb,
0x30, 0xa7, 0x61, 0xad, 0x5b, 0xe7, 0xea, 0x17, 0x3e, 0xd5, 0x94, 0x6a, 0x9b, 0xa6, 0x5a, 0xed,
0x57, 0x2e, 0xdd, 0x9c, 0x9c, 0x6f, 0xde, 0xbc, 0xc5, 0xbf, 0xbf, 0x4e, 0x98, 0xd3, 0x46, 0x77,
0xf7, 0xd7, 0x4e, 0x1d, 0xec, 0xaf, 0x81, 0xa6, 0xe1, 0x44, 0x2b, 0x1a, 0x40, 0x89, 0x86, 0xc4,
0x6d, 0x14, 0x84, 0xf6, 0xad, 0xe6, 0x03, 0xef, 0x5e, 0x53, 0x9b, 0xdd, 0x09, 0x89, 0xdb, 0x5e,
0x54, 0xb0, 0x25, 0xfe, 0x86, 0x05, 0x88, 0xfd, 0x8e, 0x05, 0xcb, 0x5a, 0xec, 0x9a, 0x47, 0x19,
0x7a, 0x65, 0x66, 0x85, 0xcd, 0x93, 0xad, 0x90, 0x7f, 0x2d, 0xd6, 0x77, 0x5a, 0x01, 0x55, 0x63,
0x8a, 0xb1, 0xba, 0xdb, 0x50, 0xf6, 0x18, 0x19, 0xd1, 0x46, 0x61, 0xbd, 0x78, 0xae, 0x7e, 0xe1,
0x72, 0x2e, 0xcb, 0x6b, 0x2f, 0x29, 0xc4, 0xf2, 0x16, 0xd7, 0x8d, 0x25, 0x84, 0xfd, 0x66, 0xc1,
0x5c, 0x1c, 0x5f, 0x35, 0x3a, 0x0f, 0x75, 0x1a, 0x8c, 0x23, 0x97, 0x60, 0x12, 0x06, 0xb4, 0x61,
0xad, 0x17, 0xcf, 0xd5, 0xda, 0x2b, 0x07, 0xfb, 0x6b, 0xf5, 0x8e, 0x26, 0x63, 0x53, 0x06, 0xfd,
0xc0, 0x82, 0xc5, 0x2e, 0xa1, 0xcc, 0xf3, 0x05, 0x7e, 0x6c, 0xf9, 0x57, 0xe6, 0xb3, 0x3c, 0x26,
0x6e, 0x6a, 0xcd, 0xed, 0xc7, 0xd5, 0x2a, 0x16, 0x0d, 0x22, 0xc5, 0x29, 0x70, 0xf4, 0x1c, 0xd4,
0xbb, 0x84, 0xba, 0x91, 0x17, 0xf2, 0xf7, 0x46, 0x71, 0xdd, 0x3a, 0x57, 0x6b, 0x3f, 0xa6, 0x3e,
0xac, 0x6f, 0x6a, 0x16, 0x36, 0xe5, 0xec, 0x3f, 0x17, 0xa1, 0x6e, 0xa0, 0x3e, 0x82, 0x30, 0x1e,
0xa6, 0xc2, 0xf8, 0x85, 0x7c, 0xbc, 0x75, 0x54, 0x1c, 0x23, 0x06, 0x15, 0xca, 0x1c, 0x36, 0xa6,
0xc2, 0x23, 0xf5, 0x0b, 0xd7, 0x72, 0xc2, 0x13, 0x3a, 0xdb, 0xcb, 0x0a, 0xb1, 0x22, 0xdf, 0xb1,
0xc2, 0x42, 0xaf, 0x41, 0x2d, 0x08, 0x79, 0xb6, 0xe0, 0x5b, 0x51, 0x12, 0xc0, 0x9b, 0x73, 0x00,
0xdf, 0x8c, 0x75, 0xb5, 0x97, 0x0e, 0xf6, 0xd7, 0x6a, 0xc9, 0x2b, 0xd6, 0x28, 0xb6, 0x0b, 0x8f,
0x1b, 0xf6, 0x6d, 0x04, 0x7e, 0xd7, 0x13, 0x1b, 0xba, 0x0e, 0x25, 0x36, 0x0d, 0x89, 0xd8, 0xcc,
0x9a, 0x76, 0xd1, 0xee, 0x34, 0x24, 0x58, 0x70, 0xd0, 0x27, 0x60, 0x61, 0x44, 0x28, 0x75, 0x7a,
0x44, 0xec, 0x49, 0xad, 0xbd, 0xa2, 0x84, 0x16, 0xae, 0x4b, 0x32, 0x8e, 0xf9, 0xf6, 0x6b, 0xf0,
0xe4, 0xe1, 0x21, 0x8a, 0x3e, 0x06, 0x15, 0x4a, 0xa2, 0x09, 0x89, 0x14, 0x90, 0xf6, 0x8c, 0xa0,
0x62, 0xc5, 0x45, 0x2d, 0xa8, 0xf9, 0xce, 0x88, 0xd0, 0xd0, 0x71, 0x63, 0xb8, 0x33, 0x4a, 0xb4,
0x76, 0x23, 0x66, 0x60, 0x2d, 0x63, 0xff, 0xc3, 0x82, 0x15, 0x03, 0xf3, 0x11, 0x64, 0xa2, 0x41,
0x3a, 0x13, 0x5d, 0xc9, 0x27, 0x62, 0x8e, 0x48, 0x45, 0x7f, 0x2c, 0xc2, 0x19, 0x33, 0xae, 0x44,
0x7e, 0xe1, 0x5b, 0x12, 0x91, 0x30, 0x78, 0x11, 0x5f, 0x53, 0xee, 0x4c, 0xb6, 0x04, 0x4b, 0x32,
0x8e, 0xf9, 0x7c, 0x7f, 0x43, 0x87, 0xf5, 0x95, 0x2f, 0x93, 0xfd, 0xdd, 0x71, 0x58, 0x1f, 0x0b,
0x0e, 0xcf, 0x0c, 0xc4, 0x9f, 0x78, 0x51, 0xe0, 0x8f, 0x88, 0xcf, 0xb2, 0x99, 0xe1, 0xb2, 0x66,
0x61, 0x53, 0x0e, 0x7d, 0x19, 0x96, 0x99, 0x13, 0xf5, 0x08, 0xc3, 0x64, 0xe2, 0xd1, 0x38, 0x90,
0x6b, 0xed, 0x27, 0xd5, 0x97, 0xcb, 0xbb, 0x29, 0x2e, 0xce, 0x48, 0xa3, 0xdf, 0x59, 0xf0, 0x94,
0x1b, 0x8c, 0xc2, 0xc0, 0x27, 0x3e, 0xdb, 0x71, 0x22, 0x67, 0x44, 0x18, 0x89, 0x6e, 0x4e, 0x48,
0x14, 0x79, 0x5d, 0x42, 0x1b, 0x65, 0xe1, 0xdd, 0xeb, 0x73, 0x78, 0x77, 0x63, 0x46, 0x7b, 0xfb,
0x69, 0x65, 0xdc, 0x53, 0x1b, 0x47, 0x23, 0xe3, 0xff, 0x66, 0x16, 0x2f, 0x04, 0x13, 0x67, 0x38,
0x26, 0xf4, 0x8a, 0x37, 0x24, 0xb4, 0x51, 0xd1, 0x85, 0xe0, 0x25, 0x4d, 0xc6, 0xa6, 0x8c, 0xfd,
0x87, 0x42, 0x2a, 0x44, 0x3b, 0x71, 0xde, 0x11, 0x7b, 0xa9, 0x02, 0x34, 0xaf, 0xbc, 0x23, 0x74,
0x1a, 0xa7, 0x4b, 0xd6, 0x23, 0x85, 0x85, 0xde, 0xb4, 0x44, 0x15, 0x88, 0x4f, 0xa5, 0xca, 0xb1,
0x0f, 0xa1, 0x22, 0x99, 0x85, 0x25, 0x26, 0x62, 0x13, 0x9a, 0x87, 0x70, 0x28, 0xeb, 0xab, 0x8a,
0xb8, 0x24, 0x84, 0x55, 0xd9, 0xc5, 0x31, 0xdf, 0xfe, 0x45, 0x25, 0x7d, 0x06, 0x64, 0x0e, 0xfd,
0x89, 0x05, 0xa7, 0xf9, 0x46, 0x39, 0x91, 0x47, 0x03, 0x1f, 0x13, 0x3a, 0x1e, 0x32, 0xe5, 0xcc,
0xed, 0x39, 0x83, 0xc6, 0x54, 0xd9, 0x6e, 0x28, 0xbb, 0x4e, 0x67, 0x39, 0x78, 0x06, 0x1e, 0x31,
0x58, 0xe8, 0x7b, 0x94, 0x05, 0xd1, 0x54, 0x25, 0x87, 0x79, 0xba, 0xb0, 0x4d, 0x12, 0x0e, 0x83,
0x29, 0x3f, 0x6b, 0x5b, 0xfe, 0x5e, 0xa0, 0xfd, 0x73, 0x55, 0x22, 0xe0, 0x18, 0x0a, 0x7d, 0xdb,
0x02, 0x08, 0xe3, 0x48, 0xe5, 0x85, 0xec, 0x21, 0x1c, 0x9c, 0xa4, 0x66, 0x27, 0x24, 0x8a, 0x0d,
0x50, 0x14, 0x40, 0xa5, 0x4f, 0x9c, 0x21, 0xeb, 0xab, 0x72, 0xf6, 0xfc, 0x1c, 0xf0, 0x57, 0x85,
0xa2, 0x6c, 0x09, 0x95, 0x54, 0xac, 0x60, 0xd0, 0x77, 0x2d, 0x58, 0x4e, 0xaa, 0x1b, 0x97, 0x25,
0x8d, 0xf2, 0xdc, 0x8d, 0xef, 0xcd, 0x94, 0xc2, 0x36, 0xe2, 0x69, 0x2c, 0x4d, 0xc3, 0x19, 0x50,
0xf4, 0x1d, 0x0b, 0xc0, 0x8d, 0xab, 0xa9, 0xcc, 0x07, 0xf5, 0x0b, 0x37, 0xf3, 0x39, 0x51, 0x49,
0x95, 0xd6, 0xee, 0x4f, 0x48, 0x14, 0x1b, 0xb0, 0xf6, 0xbb, 0x16, 0x3c, 0x61, 0x7c, 0xf8, 0x55,
0x87, 0xb9, 0xfd, 0xcb, 0x13, 0x9e, 0xa6, 0xb7, 0x53, 0xf5, 0xfd, 0x73, 0x66, 0x7d, 0x7f, 0x7f,
0x7f, 0xed, 0xe3, 0x47, 0x4d, 0x36, 0x77, 0xb8, 0x86, 0xa6, 0x50, 0x61, 0xb4, 0x02, 0xaf, 0x43,
0xdd, 0xb0, 0x59, 0xa5, 0x8f, 0xbc, 0x0a, 0x60, 0x92, 0x33, 0x0c, 0x22, 0x36, 0xf1, 0xec, 0xbf,
0x16, 0x60, 0x61, 0x63, 0x38, 0xa6, 0x8c, 0x44, 0x27, 0x6e, 0x28, 0xd6, 0xa1, 0xc4, 0x9b, 0x85,
0x6c, 0xfd, 0xe3, 0xbd, 0x04, 0x16, 0x1c, 0x14, 0x42, 0xc5, 0x0d, 0xfc, 0x3d, 0xaf, 0xa7, 0x5a,
0xc0, 0xab, 0xf3, 0x9c, 0x1c, 0x69, 0xdd, 0x86, 0xd0, 0xa7, 0x6d, 0x92, 0xef, 0x58, 0xe1, 0xa0,
0x1f, 0x59, 0xb0, 0xe2, 0x06, 0xbe, 0x4f, 0x5c, 0x1d, 0xbc, 0xa5, 0xb9, 0xdb, 0xdd, 0x8d, 0xb4,
0xc6, 0xf6, 0x87, 0x14, 0xfa, 0x4a, 0x86, 0x81, 0xb3, 0xd8, 0xf6, 0x6f, 0x0b, 0xb0, 0x94, 0xb2,
0x1c, 0x3d, 0x03, 0xd5, 0x31, 0x25, 0x91, 0xf0, 0x9c, 0xf4, 0x6f, 0xd2, 0x11, 0xbd, 0xa8, 0xe8,
0x38, 0x91, 0xe0, 0xd2, 0xa1, 0x43, 0xe9, 0x9d, 0x20, 0xea, 0x2a, 0x3f, 0x27, 0xd2, 0x3b, 0x8a,
0x8e, 0x13, 0x09, 0xde, 0x6f, 0xdc, 0x22, 0x4e, 0x44, 0xa2, 0xdd, 0x60, 0x40, 0x66, 0x26, 0x91,
0xb6, 0x66, 0x61, 0x53, 0x4e, 0x38, 0x8d, 0x0d, 0xe9, 0xc6, 0xd0, 0x23, 0x3e, 0x93, 0x66, 0xe6,
0xe0, 0xb4, 0xdd, 0x6b, 0x1d, 0x53, 0xa3, 0x76, 0x5a, 0x86, 0x81, 0xb3, 0xd8, 0xf6, 0x5f, 0x2c,
0xa8, 0x2b, 0xa7, 0x3d, 0x82, 0xa6, 0xb3, 0x97, 0x6e, 0x3a, 0xdb, 0xf3, 0xc7, 0xe8, 0x11, 0x0d,
0xe7, 0xaf, 0x8b, 0x30, 0x53, 0xe9, 0xd0, 0xab, 0x3c, 0xc7, 0x71, 0x1a, 0xe9, 0x5e, 0x8a, 0x8b,
0xec, 0x27, 0x4f, 0xb6, 0xba, 0x5d, 0x6f, 0x44, 0xcc, 0xf4, 0x15, 0x6b, 0xc1, 0x86, 0x46, 0xf4,
0x86, 0xa5, 0x01, 0x76, 0x03, 0x95, 0x57, 0xf2, 0x6d, 0x89, 0x66, 0x4c, 0xd8, 0x0d, 0xb0, 0x81,
0x89, 0xbe, 0x90, 0x0c, 0x82, 0x65, 0x11, 0x90, 0x76, 0x7a, 0x74, 0x7b, 0x3f, 0xd5, 0x00, 0x64,
0xc6, 0xb9, 0x29, 0xd4, 0x22, 0x22, 0x5b, 0xac, 0xb8, 0x02, 0xcc, 0x93, 0x44, 0xb0, 0xd2, 0x25,
0x8f, 0x71, 0x32, 0xfe, 0xc4, 0x64, 0x8a, 0x35, 0x9a, 0xfd, 0x43, 0x0b, 0xd0, 0x6c, 0xb9, 0xe6,
0x63, 0x54, 0xd2, 0xc4, 0xaa, 0x03, 0x9c, 0xe8, 0x49, 0xc4, 0xb1, 0x96, 0x39, 0x41, 0x9a, 0x7c,
0x1a, 0xca, 0xa2, 0xa9, 0x55, 0x07, 0x36, 0x89, 0x1e, 0xd1, 0xf6, 0x62, 0xc9, 0xb3, 0xff, 0x64,
0x41, 0x36, 0xdd, 0x88, 0x4c, 0x2d, 0x3d, 0x9b, 0xcd, 0xd4, 0x69, 0x2f, 0x9e, 0x7c, 0xce, 0x44,
0xaf, 0x40, 0xdd, 0x61, 0x8c, 0x8c, 0x42, 0x26, 0x02, 0xb2, 0x78, 0xdf, 0x01, 0xb9, 0xcc, 0x23,
0xe1, 0x7a, 0xd0, 0xf5, 0xf6, 0x3c, 0x11, 0x8c, 0xa6, 0x3a, 0xfb, 0xbd, 0x22, 0x2c, 0xa7, 0x9b,
0x2f, 0x34, 0x86, 0x8a, 0x68, 0x76, 0xe4, 0xcd, 0x4f, 0xee, 0xdd, 0x55, 0xe2, 0x12, 0x41, 0xa2,
0x58, 0x81, 0xf1, 0xc4, 0x1a, 0xc5, 0xd3, 0x55, 0x26, 0xb1, 0x26, 0x73, 0x55, 0x22, 0x71, 0xec,
0x44, 0x55, 0xfc, 0xff, 0x9c, 0xa8, 0x5e, 0x05, 0xe8, 0x0a, 0x6f, 0x8b, 0xbd, 0x2c, 0x3d, 0x78,
0x72, 0xd9, 0x4c, 0xb4, 0x60, 0x43, 0x23, 0x3a, 0x0b, 0x05, 0xaf, 0x2b, 0x4e, 0x75, 0xb1, 0x0d,
0x4a, 0xb6, 0xb0, 0xb5, 0x89, 0x0b, 0x5e, 0xd7, 0xa6, 0xb0, 0x68, 0x76, 0x9b, 0x27, 0x8e, 0xd5,
0x2f, 0xc2, 0x92, 0x7c, 0xda, 0x24, 0xcc, 0xf1, 0x86, 0x54, 0xed, 0xce, 0x13, 0x4a, 0x7c, 0xa9,
0x63, 0x32, 0x71, 0x5a, 0xd6, 0xfe, 0x79, 0x01, 0xe0, 0x6a, 0x10, 0x0c, 0x14, 0x66, 0x7c, 0xf4,
0xac, 0x23, 0x8f, 0xde, 0x3a, 0x94, 0x06, 0x9e, 0xdf, 0xcd, 0x1e, 0xce, 0x6d, 0xcf, 0xef, 0x62,
0xc1, 0x41, 0x17, 0x00, 0x9c, 0xd0, 0x7b, 0x89, 0x44, 0x54, 0x5f, 0xee, 0x25, 0x7e, 0xb9, 0xb4,
0xb3, 0xa5, 0x38, 0xd8, 0x90, 0x42, 0xcf, 0xa8, 0xce, 0x50, 0x8e, 0xed, 0x8d, 0x4c, 0x67, 0x58,
0xe5, 0x16, 0x1a, 0xad, 0xdf, 0xc5, 0x4c, 0x7e, 0x5c, 0x9f, 0xc9, 0x8f, 0xba, 0x53, 0xde, 0xe9,
0x3b, 0x94, 0x1c, 0x76, 0xae, 0x2b, 0xc7, 0xdc, 0x1f, 0xfd, 0xcb, 0x02, 0x7d, 0x7b, 0x85, 0xf6,
0xa0, 0x44, 0xa7, 0xbe, 0xab, 0xea, 0xcd, 0x3c, 0x19, 0xb5, 0x33, 0xf5, 0x5d, 0x7d, 0x49, 0x56,
0x15, 0x77, 0x80, 0x53, 0xdf, 0xc5, 0x42, 0x3f, 0x9a, 0x40, 0x35, 0x0a, 0x86, 0xc3, 0x5b, 0x8e,
0x3b, 0xc8, 0xa1, 0xf4, 0x60, 0xa5, 0x4a, 0xe3, 0x2d, 0x8a, 0xf3, 0xaa, 0xc8, 0x38, 0xc1, 0xb2,
0x7f, 0x53, 0x86, 0xcc, 0x74, 0x81, 0xc6, 0xe6, 0xc5, 0xa0, 0x95, 0xe3, 0xc5, 0x60, 0x92, 0xfd,
0x0f, 0xbb, 0x1c, 0x44, 0xcf, 0x41, 0x39, 0xe4, 0x7b, 0xa6, 0x22, 0x6c, 0x2d, 0xce, 0xed, 0x62,
0x23, 0x0f, 0xd9, 0x5a, 0x29, 0x6d, 0xee, 0x6c, 0xf1, 0x98, 0x8c, 0xfd, 0x2d, 0x00, 0xee, 0x6b,
0x35, 0xa6, 0xcb, 0x43, 0x7e, 0x23, 0xaf, 0x1d, 0x55, 0x93, 0xba, 0x48, 0xea, 0x9d, 0x04, 0x05,
0x1b, 0x88, 0xe8, 0xfb, 0x16, 0x2c, 0xc7, 0x8e, 0x57, 0x46, 0x94, 0x1f, 0x8a, 0x11, 0x62, 0x66,
0xc4, 0x29, 0x24, 0x9c, 0x41, 0x46, 0x5f, 0x83, 0x1a, 0x65, 0x4e, 0x24, 0x8b, 0x57, 0xe5, 0xbe,
0x13, 0x5e, 0xb2, 0x97, 0x9d, 0x58, 0x09, 0xd6, 0xfa, 0xd0, 0xcb, 0x00, 0x7b, 0x9e, 0xef, 0xd1,
0xbe, 0xd0, 0xbe, 0xf0, 0x60, 0xa5, 0xf1, 0x4a, 0xa2, 0x01, 0x1b, 0xda, 0xec, 0xbf, 0x15, 0x00,
0xc4, 0xcf, 0x0d, 0x4f, 0x5c, 0x3c, 0xac, 0x43, 0x29, 0x22, 0x61, 0x90, 0xcd, 0x5c, 0x5c, 0x02,
0x0b, 0x4e, 0x6a, 0x8e, 0x28, 0xdc, 0xd7, 0x1c, 0x51, 0x3c, 0x76, 0x8e, 0xe0, 0x39, 0x98, 0xf6,
0x77, 0x22, 0x6f, 0xe2, 0x30, 0xb2, 0x4d, 0xa6, 0x2a, 0x91, 0xe9, 0x1c, 0xdc, 0xb9, 0xaa, 0x99,
0x38, 0x2d, 0x7b, 0xe8, 0x08, 0x56, 0xfe, 0x1f, 0x8e, 0x60, 0xef, 0x58, 0xb0, 0xac, 0x3d, 0xfb,
0xc1, 0xfa, 0x9f, 0xa6, 0xed, 0x3e, 0x62, 0xa6, 0xf8, 0xb7, 0x05, 0x2b, 0x71, 0xf7, 0xaa, 0x8a,
0x60, 0x2e, 0x55, 0x2f, 0xf5, 0xb3, 0xa0, 0x78, 0xfc, 0xcf, 0x02, 0x33, 0x61, 0x95, 0x8e, 0x49,
0x58, 0x5f, 0xc8, 0xd4, 0xbb, 0x0f, 0xcd, 0xd4, 0x3b, 0x94, 0xf4, 0xe9, 0x53, 0xdf, 0x4d, 0xf7,
0x07, 0xf6, 0x2f, 0x2d, 0x58, 0x8c, 0xd9, 0x37, 0x82, 0xae, 0xe8, 0x9e, 0xa9, 0x08, 0x32, 0x2b,
0x58, 0x5f, 0xca, 0xd4, 0xbb, 0x8f, 0xcc, 0xd4, 0x3b, 0x94, 0xf4, 0xe9, 0x53, 0xdf, 0x4d, 0xf7,
0x07, 0xf6, 0xaf, 0x2c, 0x58, 0x8c, 0xd9, 0x37, 0x82, 0xae, 0xe8, 0x9e, 0xa9, 0x08, 0x32, 0x2b,
0xdd, 0x3d, 0xcb, 0x70, 0x90, 0x3c, 0x34, 0x86, 0xaa, 0xdb, 0xf7, 0x86, 0xdd, 0x88, 0xf8, 0x6a,
0x5b, 0x5e, 0xcc, 0x61, 0x8c, 0xe0, 0xf8, 0x3a, 0x14, 0x36, 0x14, 0x00, 0x4e, 0xa0, 0xec, 0xdf,
0x15, 0x61, 0x29, 0x35, 0x73, 0xf0, 0x11, 0x5d, 0xde, 0xd6, 0x77, 0x0c, 0x9b, 0x93, 0x11, 0x7d,
0x5b, 0x9e, 0xcf, 0x61, 0x8c, 0xe0, 0xf8, 0x3a, 0x14, 0x36, 0x14, 0x00, 0x4e, 0xa0, 0xec, 0xdf,
0x17, 0x61, 0x29, 0x35, 0x73, 0xf0, 0x11, 0x5d, 0xde, 0xd6, 0x77, 0x0c, 0x9b, 0x93, 0x11, 0x7d,
0x57, 0xb3, 0xb0, 0x29, 0xc7, 0xf7, 0x63, 0xe8, 0x4d, 0xa4, 0x8e, 0xec, 0xcf, 0x9b, 0x6b, 0x31,
0x03, 0x6b, 0x19, 0x63, 0xe8, 0x2a, 0x3e, 0xf0, 0xd0, 0xf5, 0x13, 0x0b, 0x90, 0x58, 0x02, 0xd7,
0x03, 0x6b, 0x19, 0x63, 0xe8, 0x2a, 0xde, 0xf7, 0xd0, 0xf5, 0x53, 0x0b, 0x90, 0x58, 0x02, 0xd7,
0x9c, 0xcc, 0x46, 0x8d, 0x52, 0xbe, 0x7e, 0x3b, 0xab, 0x2c, 0x42, 0x1b, 0x33, 0x50, 0xf8, 0x10,
0x78, 0xe3, 0x1e, 0xb4, 0xfc, 0x58, 0xee, 0x41, 0xed, 0xaf, 0xc3, 0x99, 0x99, 0x8e, 0x43, 0xb5,
0x78, 0xe3, 0x1e, 0xb4, 0xfc, 0x48, 0xee, 0x41, 0xed, 0x6f, 0xc2, 0x99, 0x99, 0x8e, 0x43, 0xb5,
0xbc, 0xd6, 0x61, 0x2d, 0x2f, 0x8f, 0xc4, 0x30, 0x1a, 0xfb, 0x72, 0x83, 0xaa, 0x3a, 0x12, 0x77,
0x38, 0x11, 0x4b, 0x1e, 0xef, 0x83, 0xbb, 0xd1, 0x14, 0x8f, 0x65, 0x2f, 0x59, 0xd5, 0xe8, 0x9b,
0x82, 0x8a, 0x15, 0xd7, 0xfe, 0x6e, 0x01, 0x96, 0x52, 0x55, 0x30, 0x35, 0xb2, 0x58, 0xc7, 0x8e,
0x2c, 0x79, 0x1a, 0x83, 0xde, 0x84, 0x45, 0x2a, 0x8e, 0x62, 0xe4, 0x30, 0xd2, 0x9b, 0xe6, 0x70,
0x82, 0x8a, 0x15, 0xd7, 0xfe, 0x5e, 0x01, 0x96, 0x52, 0x55, 0x30, 0x35, 0xb2, 0x58, 0xc7, 0x8e,
0x2c, 0x79, 0x1a, 0x83, 0x5e, 0x87, 0x45, 0x2a, 0x8e, 0x62, 0xe4, 0x30, 0xd2, 0x9b, 0xe6, 0x70,
0x13, 0xdd, 0x31, 0xd4, 0xb5, 0x4f, 0x1f, 0xec, 0xaf, 0x2d, 0x9a, 0x14, 0x9c, 0x82, 0xb3, 0x7f,
0x51, 0x80, 0x27, 0x0e, 0xe9, 0x08, 0xd0, 0x1d, 0xf3, 0x76, 0x40, 0x8e, 0x8f, 0x2f, 0xe5, 0x10,
0x9e, 0x2a, 0x91, 0xca, 0x5f, 0xbe, 0x87, 0xdd, 0x0d, 0x3c, 0xe0, 0xf4, 0xb8, 0x07, 0xe5, 0x7e,
0x59, 0x80, 0xc7, 0x0e, 0xe9, 0x08, 0xd0, 0x1d, 0xf3, 0x76, 0x40, 0x8e, 0x8f, 0x2f, 0xe4, 0x10,
0x9e, 0x2a, 0x91, 0xca, 0x5f, 0xbe, 0x87, 0xdd, 0x0d, 0xdc, 0xe7, 0xf4, 0xb8, 0x07, 0xe5, 0x7e,
0x10, 0x0c, 0xe2, 0x31, 0x71, 0x9e, 0x82, 0xa0, 0x87, 0x9b, 0x76, 0x8d, 0xef, 0x26, 0x7f, 0xa7,
0x58, 0xaa, 0xb7, 0xdf, 0xb3, 0x20, 0xe5, 0x45, 0x34, 0x82, 0x32, 0xd7, 0x32, 0xcd, 0xe1, 0x4f,
0x98, 0xa9, 0xf7, 0x12, 0xd7, 0x29, 0xf1, 0xc5, 0x23, 0x96, 0x28, 0xc8, 0x83, 0x12, 0x37, 0x44,
0x75, 0xfa, 0xdb, 0x39, 0xa1, 0xf1, 0x25, 0xca, 0xc1, 0x82, 0x3f, 0x61, 0x01, 0x61, 0x5f, 0x84,
0x33, 0x33, 0x16, 0xf1, 0x90, 0xdf, 0x0b, 0xe2, 0x1f, 0x7f, 0x46, 0xc8, 0x5f, 0xe1, 0x44, 0x2c,
0x79, 0xbc, 0x7e, 0x9c, 0xce, 0xaa, 0x47, 0x3f, 0xb5, 0xe0, 0x0c, 0xcd, 0xea, 0x7b, 0x24, 0x5e,
0xfb, 0xa0, 0x32, 0x6a, 0xd6, 0x7c, 0x3c, 0x6b, 0x01, 0xdf, 0xd1, 0xec, 0x75, 0x29, 0x8f, 0x3d,
0x79, 0xbc, 0x7e, 0x9c, 0xce, 0xaa, 0x47, 0x3f, 0xb3, 0xe0, 0x0c, 0xcd, 0xea, 0x7b, 0x28, 0x5e,
0xfb, 0xb0, 0x32, 0x6a, 0xd6, 0x7c, 0x3c, 0x6b, 0x01, 0xdf, 0xd1, 0xec, 0x75, 0x29, 0x8f, 0x3d,
0xcf, 0xa7, 0xc4, 0x1d, 0x47, 0xf1, 0x42, 0x93, 0xd8, 0xdb, 0x52, 0x74, 0x9c, 0x48, 0xf0, 0xf1,
0x55, 0x5e, 0xd7, 0xdf, 0xd0, 0x8d, 0x62, 0x32, 0xbe, 0x76, 0x12, 0x0e, 0x36, 0xa4, 0xd0, 0x39,
0xa8, 0xba, 0x24, 0x62, 0x9b, 0xbc, 0x3d, 0xe2, 0x79, 0x61, 0x51, 0xce, 0x59, 0x1b, 0x8a, 0x86,
0x13, 0x2e, 0xfa, 0x30, 0x2c, 0x0c, 0xc8, 0x54, 0x08, 0x96, 0x84, 0x60, 0x9d, 0x57, 0xfc, 0x6d,
0x13, 0x2e, 0xfa, 0x28, 0x2c, 0x0c, 0xc8, 0x54, 0x08, 0x96, 0x84, 0x60, 0x9d, 0x57, 0xfc, 0x6d,
0x49, 0xc2, 0x31, 0x0f, 0xd9, 0x50, 0x71, 0x1d, 0x21, 0x55, 0x16, 0x52, 0x20, 0x6e, 0xee, 0x2f,
0x09, 0x21, 0xc5, 0x69, 0x37, 0xef, 0xdd, 0x5f, 0x3d, 0xf5, 0xd6, 0xfd, 0xd5, 0x53, 0x6f, 0xdf,
0x5f, 0x3d, 0x75, 0xf7, 0x60, 0xd5, 0xba, 0x77, 0xb0, 0x6a, 0xbd, 0x75, 0xb0, 0x6a, 0xbd, 0x7d,
0xb0, 0x6a, 0xfd, 0xe3, 0x60, 0xd5, 0xfa, 0xd1, 0xbb, 0xab, 0xa7, 0x5e, 0xad, 0xc6, 0xae, 0xfd,
0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x7a, 0x73, 0x59, 0x1b, 0x86, 0x27, 0x00, 0x00,
0x09, 0x21, 0xc5, 0x69, 0x37, 0xef, 0xde, 0x5b, 0x3d, 0xf5, 0xd6, 0xbd, 0xd5, 0x53, 0x6f, 0xdf,
0x5b, 0x3d, 0xf5, 0xc6, 0xc1, 0xaa, 0x75, 0xf7, 0x60, 0xd5, 0x7a, 0xeb, 0x60, 0xd5, 0x7a, 0xfb,
0x60, 0xd5, 0xfa, 0xe7, 0xc1, 0xaa, 0xf5, 0xe3, 0x77, 0x57, 0x4f, 0xbd, 0x5c, 0x8d, 0x5d, 0xfb,
0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xf5, 0xd2, 0xa5, 0x7e, 0x8e, 0x27, 0x00, 0x00,
}

View File

@@ -34,7 +34,7 @@ message AppProjectList {
// AppProjectSpec represents
message AppProjectSpec {
// SourceRepos contains list of git repository URLs which can be used for deployment
repeated string sources = 1;
repeated string sourceRepos = 1;
// Destinations contains list of destinations available for deployment
repeated ApplicationDestination destinations = 2;

View File

@@ -446,7 +446,7 @@ type AppProject struct {
// AppProjectSpec represents
type AppProjectSpec struct {
// SourceRepos contains list of git repository URLs which can be used for deployment
SourceRepos []string `json:"sources" protobuf:"bytes,1,name=destination"`
SourceRepos []string `json:"sourceRepos" protobuf:"bytes,1,name=sourceRepos"`
// Destinations contains list of destinations available for deployment
Destinations []ApplicationDestination `json:"destinations" protobuf:"bytes,2,name=destination"`

View File

@@ -191,13 +191,20 @@ func generateManifests(appPath string, q *ManifestRequest) (*ManifestResponse, e
var env *app.EnvironmentSpec
var err error
appSourceType := identifyAppSourceType(appPath)
appSourceType := IdentifyAppSourceTypeByAppDir(appPath)
switch appSourceType {
case AppSourceKsonnet:
targetObjs, params, env, err = ksShow(appPath, q.Environment, q.ComponentParameterOverrides)
case AppSourceHelm:
h := helm.NewHelmApp(appPath)
targetObjs, err = h.Template(q.AppLabel, q.ValueFiles, q.ComponentParameterOverrides)
if err != nil {
return nil, err
}
params, err = h.GetParameters(q.ValueFiles)
if err != nil {
return nil, err
}
case AppSourceDirectory:
targetObjs, err = findManifests(appPath)
}
@@ -237,12 +244,23 @@ func tempRepoPath(repo string) string {
return path.Join(os.TempDir(), strings.Replace(repo, "/", "_", -1))
}
// identifyAppSourceType examines a directory and determines its application source type
func identifyAppSourceType(appPath string) AppSourceType {
if pathExists(path.Join(appPath, "app.yaml")) {
// IdentifyAppSourceTypeByAppDir examines a directory and determines its application source type
func IdentifyAppSourceTypeByAppDir(appDirPath string) AppSourceType {
if pathExists(path.Join(appDirPath, "app.yaml")) {
return AppSourceKsonnet
}
if pathExists(path.Join(appPath, "Chart.yaml")) {
if pathExists(path.Join(appDirPath, "Chart.yaml")) {
return AppSourceHelm
}
return AppSourceDirectory
}
// IdentifyAppSourceTypeByAppPath determines application source type by app file path
func IdentifyAppSourceTypeByAppPath(appFilePath string) AppSourceType {
if strings.HasSuffix(appFilePath, "app.yaml") {
return AppSourceKsonnet
}
if strings.HasSuffix(appFilePath, "Chart.yaml") {
return AppSourceHelm
}
return AppSourceDirectory

View File

@@ -214,23 +214,32 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *ApplicationResourceE
if !s.enf.EnforceClaims(ctx.Value("claims"), "applications/events", "get", appRBACName(*a)) {
return nil, grpc.ErrPermissionDenied
}
config, namespace, err := s.getApplicationClusterConfig(*q.Name)
if err != nil {
return nil, err
}
kubeClientset, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
var fieldSelector string
var (
kubeClientset kubernetes.Interface
fieldSelector string
namespace string
)
// There are two places where we get events. If we are getting application events, we query
// our own cluster. If it is events on a resource on an external cluster, then we query the
// external cluster using its rest.Config
if q.ResourceName == "" && q.ResourceUID == "" {
kubeClientset = s.kubeclientset
namespace = a.Namespace
fieldSelector = fields.SelectorFromSet(map[string]string{
"involvedObject.name": a.Name,
"involvedObject.uid": string(a.UID),
"involvedObject.namespace": namespace,
"involvedObject.namespace": a.Namespace,
}).String()
} else {
var config *rest.Config
config, namespace, err = s.getApplicationClusterConfig(*q.Name)
if err != nil {
return nil, err
}
kubeClientset, err = kubernetes.NewForConfig(config)
if err != nil {
return nil, err
}
fieldSelector = fields.SelectorFromSet(map[string]string{
"involvedObject.name": q.ResourceName,
"involvedObject.uid": q.ResourceUID,
@@ -240,7 +249,6 @@ func (s *Server) ListResourceEvents(ctx context.Context, q *ApplicationResourceE
log.Infof("Querying for resource events with field selector: %s", fieldSelector)
opts := metav1.ListOptions{FieldSelector: fieldSelector}
return kubeClientset.CoreV1().Events(namespace).List(opts)
}

View File

@@ -1,6 +1,7 @@
package repository
import (
"path"
"reflect"
"github.com/ghodss/yaml"
@@ -75,84 +76,96 @@ func (s *Server) ListApps(ctx context.Context, q *RepoAppsQuery) (*RepoAppsRespo
revision = "HEAD"
}
ksonnetApps, err := s.listKsonnetApps(ctx, repo, revision, repoClient)
ksonnetRes, err := repoClient.ListDir(ctx, &repository.ListDirRequest{Repo: repo, Revision: revision, Path: "*app.yaml"})
if err != nil {
return nil, err
}
helmApps, err := s.listHelmApps(ctx, repo, revision, repoClient)
helmRes, err := repoClient.ListDir(ctx, &repository.ListDirRequest{Repo: repo, Revision: revision, Path: "*Chart.yaml"})
if err != nil {
return nil, err
}
return &RepoAppsResponse{
KsonnetApps: ksonnetApps,
HelmApps: helmApps,
}, nil
items := make([]*AppInfo, 0)
for i := range ksonnetRes.Items {
items = append(items, &AppInfo{Type: string(repository.AppSourceKsonnet), Path: ksonnetRes.Items[i]})
}
for i := range helmRes.Items {
items = append(items, &AppInfo{Type: string(repository.AppSourceHelm), Path: helmRes.Items[i]})
}
return &RepoAppsResponse{Items: items}, nil
}
func (s *Server) listHelmApps(ctx context.Context, repo *appsv1.Repository, revision string, repoClient repository.RepositoryServiceClient) ([]*HelmAppSpec, error) {
req := repository.ListDirRequest{
Repo: repo,
Revision: revision,
Path: "*Chart.yaml",
func (s *Server) GetAppDetails(ctx context.Context, q *RepoAppDetailsQuery) (*RepoAppDetailsResponse, error) {
if !s.enf.EnforceClaims(ctx.Value("claims"), "repositories/apps", "get", q.Repo) {
return nil, grpc.ErrPermissionDenied
}
getRes, err := repoClient.ListDir(ctx, &req)
repo, err := s.db.GetRepository(ctx, q.Repo)
if err != nil {
return nil, err
}
helmApps := make([]*HelmAppSpec, 0)
for _, path := range getRes.Items {
getFileRes, err := repoClient.GetFile(ctx, &repository.GetFileRequest{
Repo: repo,
Revision: revision,
Path: path,
})
if err != nil {
return nil, err
}
var appSpec HelmAppSpec
appSpec.Path = path
err = yaml.Unmarshal(getFileRes.Data, &appSpec)
if err == nil && appSpec.Name != "" {
helmApps = append(helmApps, &appSpec)
}
// Test the repo
conn, repoClient, err := s.repoClientset.NewRepositoryClient()
if err != nil {
return nil, err
}
return helmApps, nil
}
defer util.Close(conn)
func (s *Server) listKsonnetApps(ctx context.Context, repo *appsv1.Repository, revision string, repoClient repository.RepositoryServiceClient) ([]*KsonnetAppSpec, error) {
req := repository.ListDirRequest{
revision := q.Revision
if revision == "" {
revision = "HEAD"
}
appSpecRes, err := repoClient.GetFile(ctx, &repository.GetFileRequest{
Repo: repo,
Revision: revision,
Path: "*app.yaml",
}
getRes, err := repoClient.ListDir(ctx, &req)
Path: q.Path,
})
if err != nil {
return nil, err
}
ksonnetApps := make([]*KsonnetAppSpec, 0)
for _, path := range getRes.Items {
getFileRes, err := repoClient.GetFile(ctx, &repository.GetFileRequest{
Repo: repo,
Revision: revision,
Path: path,
})
if err != nil {
return nil, err
}
appSourceType := repository.IdentifyAppSourceTypeByAppPath(q.Path)
switch appSourceType {
case repository.AppSourceKsonnet:
var appSpec KsonnetAppSpec
appSpec.Path = path
err = yaml.Unmarshal(getFileRes.Data, &appSpec)
if err == nil && appSpec.Name != "" && len(appSpec.Environments) > 0 {
ksonnetApps = append(ksonnetApps, &appSpec)
appSpec.Path = q.Path
err = yaml.Unmarshal(appSpecRes.Data, &appSpec)
if err != nil {
return nil, err
}
return &RepoAppDetailsResponse{
Type: string(appSourceType),
Ksonnet: &appSpec,
}, nil
case repository.AppSourceHelm:
var appSpec HelmAppSpec
appSpec.Path = q.Path
err = yaml.Unmarshal(appSpecRes.Data, &appSpec)
if err != nil {
return nil, err
}
valuesFilesRes, err := repoClient.ListDir(ctx, &repository.ListDirRequest{
Revision: revision,
Repo: repo,
Path: path.Join(path.Dir(q.Path), "*values*.yaml"),
})
if err != nil {
return nil, err
}
appSpec.ValueFiles = valuesFilesRes.Items
return &RepoAppDetailsResponse{
Type: string(appSourceType),
Helm: &appSpec,
}, nil
}
return ksonnetApps, nil
return nil, status.Errorf(codes.InvalidArgument, "specified application path is not supported")
}
// Create creates a repository

File diff suppressed because it is too large Load Diff

View File

@@ -80,6 +80,52 @@ func request_RepositoryService_ListApps_0(ctx context.Context, marshaler runtime
}
var (
filter_RepositoryService_GetAppDetails_0 = &utilities.DoubleArray{Encoding: map[string]int{"repo": 0, "path": 1}, Base: []int{1, 1, 2, 0, 0}, Check: []int{0, 1, 1, 2, 3}}
)
func request_RepositoryService_GetAppDetails_0(ctx context.Context, marshaler runtime.Marshaler, client RepositoryServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq RepoAppDetailsQuery
var metadata runtime.ServerMetadata
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["repo"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "repo")
}
protoReq.Repo, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "repo", err)
}
val, ok = pathParams["path"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "path")
}
protoReq.Path, err = runtime.String(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "path", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.URL.Query(), filter_RepositoryService_GetAppDetails_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetAppDetails(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
var (
filter_RepositoryService_Create_0 = &utilities.DoubleArray{Encoding: map[string]int{"repo": 0}, Base: []int{1, 1, 0}, Check: []int{0, 1, 2}}
)
@@ -282,6 +328,35 @@ func RegisterRepositoryServiceHandlerClient(ctx context.Context, mux *runtime.Se
})
mux.Handle("GET", pattern_RepositoryService_GetAppDetails_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_RepositoryService_GetAppDetails_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_RepositoryService_GetAppDetails_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_RepositoryService_Create_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
@@ -406,6 +481,8 @@ var (
pattern_RepositoryService_ListApps_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "repositories", "repo", "apps"}, ""))
pattern_RepositoryService_GetAppDetails_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "repositories", "repo", "apps", "path"}, ""))
pattern_RepositoryService_Create_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "repositories"}, ""))
pattern_RepositoryService_Get_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3}, []string{"api", "v1", "repositories", "repo"}, ""))
@@ -420,6 +497,8 @@ var (
forward_RepositoryService_ListApps_0 = runtime.ForwardResponseMessage
forward_RepositoryService_GetAppDetails_0 = runtime.ForwardResponseMessage
forward_RepositoryService_Create_0 = runtime.ForwardResponseMessage
forward_RepositoryService_Get_0 = runtime.ForwardResponseMessage

View File

@@ -17,10 +17,30 @@ message RepoAppsQuery {
string revision = 2;
}
// AppInfo contains application type and app file path
message AppInfo {
string type = 1;
string path = 2;
}
// RepoAppDetailsQuery contains query information for app details request
message RepoAppDetailsQuery {
string repo = 1;
string revision = 2;
string path = 3;
}
// RepoAppDetailsResponse application details
message RepoAppDetailsResponse {
string type = 1;
KsonnetAppSpec ksonnet = 2;
HelmAppSpec helm = 3;
}
// RepoAppsResponse contains applications of specified repository
message RepoAppsResponse {
repeated KsonnetAppSpec ksonnetApps = 1;
repeated HelmAppSpec helmApps = 2;
repeated AppInfo items = 1;
}
// KsonnetAppSpec contains Ksonnet app response
@@ -35,6 +55,7 @@ message KsonnetAppSpec {
message HelmAppSpec {
string name = 1;
string path = 2;
repeated string valueFiles = 3;
}
message KsonnetEnvironment {
@@ -79,11 +100,16 @@ service RepositoryService {
option (google.api.http).get = "/api/v1/repositories";
}
// ListKsonnetApps returns list of Ksonnet apps in the repo
// ListApps returns list of apps in the repo
rpc ListApps(RepoAppsQuery) returns (RepoAppsResponse) {
option (google.api.http).get = "/api/v1/repositories/{repo}/apps";
}
// GetAppDetails returns application details by given path
rpc GetAppDetails(RepoAppDetailsQuery) returns (RepoAppDetailsResponse) {
option (google.api.http).get = "/api/v1/repositories/{repo}/apps/{path}";
}
// Create creates a repo
rpc Create(RepoCreateRequest) returns (github.com.argoproj.argo_cd.pkg.apis.application.v1alpha1.Repository) {
option (google.api.http) = {

View File

@@ -8,6 +8,7 @@ import (
"net/http"
"net/url"
"os"
"regexp"
"strings"
"time"
@@ -520,22 +521,17 @@ type bug21955Workaround struct {
handler http.Handler
}
var pathPatters = []*regexp.Regexp{
regexp.MustCompile(`/api/v1/clusters/[^/]+`),
regexp.MustCompile(`/api/v1/repositories/[^/]+`),
regexp.MustCompile(`/api/v1/repositories/[^/]+/apps`),
regexp.MustCompile(`/api/v1/repositories/[^/]+/apps/[^/]+`),
}
func (bf *bug21955Workaround) ServeHTTP(w http.ResponseWriter, r *http.Request) {
paths := map[string][]string{
"/api/v1/repositories/": {"apps"},
"/api/v1/clusters/": {},
}
for path, subPaths := range paths {
if strings.Index(r.URL.Path, path) > -1 {
postfix := ""
for _, subPath := range subPaths {
if strings.LastIndex(r.URL.Path, subPath) == len(r.URL.Path)-len(subPath) {
postfix = "/" + subPath
r.URL.Path = r.URL.Path[0 : len(r.URL.Path)-len(subPath)-1]
break
}
}
r.URL.Path = path + url.QueryEscape(r.URL.Path[len(path):]) + postfix
for _, pattern := range pathPatters {
if pattern.MatchString(r.URL.RawPath) {
r.URL.Path = r.URL.RawPath
break
}
}
@@ -555,6 +551,17 @@ func bug21955WorkaroundInterceptor(ctx context.Context, req interface{}, _ *grpc
return nil, err
}
rk.Repo = repo
} else if rdq, ok := req.(*repository.RepoAppDetailsQuery); ok {
repo, err := url.QueryUnescape(rdq.Repo)
if err != nil {
return nil, err
}
path, err := url.QueryUnescape(rdq.Path)
if err != nil {
return nil, err
}
rdq.Repo = repo
rdq.Path = path
} else if ru, ok := req.(*repository.RepoUpdateRequest); ok {
repo, err := url.QueryUnescape(ru.Repo.Repo)
if err != nil {

View File

@@ -884,7 +884,7 @@
"tags": [
"RepositoryService"
],
"summary": "ListKsonnetApps returns list of Ksonnet apps in the repo",
"summary": "ListApps returns list of apps in the repo",
"operationId": "ListApps",
"parameters": [
{
@@ -909,6 +909,42 @@
}
}
},
"/api/v1/repositories/{repo}/apps/{path}": {
"get": {
"tags": [
"RepositoryService"
],
"summary": "GetAppDetails returns application details by given path",
"operationId": "GetAppDetails",
"parameters": [
{
"type": "string",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"name": "path",
"in": "path",
"required": true
},
{
"type": "string",
"name": "revision",
"in": "query"
}
],
"responses": {
"200": {
"description": "(empty)",
"schema": {
"$ref": "#/definitions/repositoryRepoAppDetailsResponse"
}
}
}
}
},
"/api/v1/session": {
"post": {
"tags": [
@@ -1236,6 +1272,18 @@
}
}
},
"repositoryAppInfo": {
"type": "object",
"title": "AppInfo contains application type and app file path",
"properties": {
"path": {
"type": "string"
},
"type": {
"type": "string"
}
}
},
"repositoryHelmAppSpec": {
"type": "object",
"title": "HelmAppSpec contains helm app name and path in source repo",
@@ -1245,6 +1293,12 @@
},
"path": {
"type": "string"
},
"valueFiles": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
@@ -1325,20 +1379,29 @@
}
}
},
"repositoryRepoAppDetailsResponse": {
"type": "object",
"title": "RepoAppDetailsResponse application details",
"properties": {
"helm": {
"$ref": "#/definitions/repositoryHelmAppSpec"
},
"ksonnet": {
"$ref": "#/definitions/repositoryKsonnetAppSpec"
},
"type": {
"type": "string"
}
}
},
"repositoryRepoAppsResponse": {
"type": "object",
"title": "RepoAppsResponse contains applications of specified repository",
"properties": {
"helmApps": {
"items": {
"type": "array",
"items": {
"$ref": "#/definitions/repositoryHelmAppSpec"
}
},
"ksonnetApps": {
"type": "array",
"items": {
"$ref": "#/definitions/repositoryKsonnetAppSpec"
"$ref": "#/definitions/repositoryAppInfo"
}
}
}
@@ -1742,7 +1805,7 @@
"$ref": "#/definitions/v1alpha1ApplicationDestination"
}
},
"sources": {
"sourceRepos": {
"type": "array",
"title": "SourceRepos contains list of git repository URLs which can be used for deployment",
"items": {

View File

@@ -170,7 +170,7 @@ func TestAppManagement(t *testing.T) {
WaitUntil(t, func() (done bool, err error) {
app, err := fixture.AppClient.ArgoprojV1alpha1().Applications(fixture.Namespace).Get(app.ObjectMeta.Name, metav1.GetOptions{})
return err == nil && app.Status.ComparisonResult.Status != v1alpha1.ComparisonStatusUnknown && len(app.Status.Conditions) > 0, err
return err == nil && app.Status.ComparisonResult.Status == v1alpha1.ComparisonStatusUnknown && len(app.Status.Conditions) > 0, err
})
app, err := fixture.AppClient.ArgoprojV1alpha1().Applications(fixture.Namespace).Get(app.ObjectMeta.Name, metav1.GetOptions{})

View File

@@ -17,7 +17,8 @@ func TestMain(m *testing.M) {
println(fmt.Sprintf("Unable to create e2e fixture: %v", err))
os.Exit(-1)
} else {
defer fixture.TearDown()
m.Run()
code := m.Run()
fixture.TearDown()
os.Exit(code)
}
}

View File

@@ -308,11 +308,13 @@ func queryAppSourceType(ctx context.Context, spec *argoappv1.ApplicationSpec, re
return "", err
}
for _, gitPath := range getRes.Items {
// gitPath may look like: app.yaml, or some/subpath/app.yaml
trimmedPath := strings.TrimPrefix(gitPath, spec.Source.Path)
if trimmedPath == "/app.yaml" {
trimmedPath = strings.TrimPrefix(trimmedPath, "/")
if trimmedPath == "app.yaml" {
return repository.AppSourceKsonnet, nil
}
if trimmedPath == "/Chart.yaml" {
if trimmedPath == "Chart.yaml" {
return repository.AppSourceHelm, nil
}
}

View File

@@ -2,10 +2,12 @@ package helm
import (
"fmt"
"io/ioutil"
"os/exec"
"path"
"strings"
"github.com/ghodss/yaml"
log "github.com/sirupsen/logrus"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -16,7 +18,9 @@ import (
// Helm provides wrapper functionality around the `helm` command.
type Helm interface {
// Template returns a list of unstructured objects from a `helm template` command
Template(name string, valuesFiles []string, params []*argoappv1.ComponentParameter) ([]*unstructured.Unstructured, error)
Template(name string, valuesFiles []string, overrides []*argoappv1.ComponentParameter) ([]*unstructured.Unstructured, error)
// GetParameters returns a list of chart parameters taking into account values in provided YAML files.
GetParameters(valuesFiles []string) ([]*argoappv1.ComponentParameter, error)
}
// NewHelmApp create a new wrapper to run commands on the `helm` command-line tool.
@@ -28,16 +32,57 @@ type helm struct {
path string
}
func (h *helm) Template(name string, valuesFiles []string, params []*argoappv1.ComponentParameter) ([]*unstructured.Unstructured, error) {
func (h *helm) Template(name string, valuesFiles []string, overrides []*argoappv1.ComponentParameter) ([]*unstructured.Unstructured, error) {
args := []string{
"template", h.path, "--name", name,
}
for _, valuesFile := range valuesFiles {
args = append(args, "-f", path.Join(h.path, valuesFile))
}
for _, p := range params {
for _, p := range overrides {
args = append(args, "--set", fmt.Sprintf("%s=%s", p.Name, p.Value))
}
out, err := helmCmd(args...)
if err != nil {
return nil, err
}
return kube.SplitYAML(out)
}
func (h *helm) GetParameters(valuesFiles []string) ([]*argoappv1.ComponentParameter, error) {
out, err := helmCmd("inspect", "values", h.path)
if err != nil {
return nil, err
}
values := append([]string{out})
for _, file := range valuesFiles {
fileValues, err := ioutil.ReadFile(path.Join(h.path, file))
if err != nil {
return nil, fmt.Errorf("failed to read value file %s: %s", file, err)
}
values = append(values, string(fileValues))
}
output := map[string]string{}
for _, file := range values {
values := map[string]interface{}{}
if err = yaml.Unmarshal([]byte(file), &values); err != nil {
return nil, fmt.Errorf("failed to parse values: %s", err)
}
flatVals(values, output)
}
params := make([]*argoappv1.ComponentParameter, 0)
for key, val := range output {
params = append(params, &argoappv1.ComponentParameter{
Name: key,
Value: val,
})
}
return params, nil
}
func helmCmd(args ...string) (string, error) {
cmd := exec.Command("helm", args...)
cmdStr := strings.Join(cmd.Args, " ")
log.Info(cmdStr)
@@ -45,13 +90,23 @@ func (h *helm) Template(name string, valuesFiles []string, params []*argoappv1.C
if err != nil {
exErr, ok := err.(*exec.ExitError)
if !ok {
return nil, err
return "", err
}
errOutput := string(exErr.Stderr)
log.Errorf("`%s` failed: %s", cmdStr, errOutput)
return nil, fmt.Errorf(strings.TrimSpace(errOutput))
return "", fmt.Errorf(strings.TrimSpace(errOutput))
}
out := string(outBytes)
log.Debug(out)
return kube.SplitYAML(out)
return out, nil
}
func flatVals(input map[string]interface{}, output map[string]string, prefixes ...string) {
for key, val := range input {
if subMap, ok := val.(map[string]interface{}); ok {
flatVals(subMap, output, append(prefixes, fmt.Sprintf("%v", key))...)
} else {
output[strings.Join(append(prefixes, fmt.Sprintf("%v", key)), ".")] = fmt.Sprintf("%v", val)
}
}
}

View File

@@ -11,9 +11,18 @@ import (
argoappv1 "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
)
func findParameter(params []*argoappv1.ComponentParameter, name string) *argoappv1.ComponentParameter {
for _, param := range params {
if param.Name == name {
return param
}
}
return nil
}
func TestHelmTemplateParams(t *testing.T) {
h := NewHelmApp("./testdata/minio")
params := []*argoappv1.ComponentParameter{
overrides := []*argoappv1.ComponentParameter{
{
Name: "service.type",
Value: "LoadBalancer",
@@ -23,7 +32,7 @@ func TestHelmTemplateParams(t *testing.T) {
Value: "1234",
},
}
objs, err := h.Template("test", nil, params)
objs, err := h.Template("test", nil, overrides)
assert.Nil(t, err)
assert.Equal(t, 5, len(objs))
@@ -51,7 +60,27 @@ func TestHelmTemplateValues(t *testing.T) {
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &dep)
assert.Nil(t, err)
assert.Equal(t, int32(3), *dep.Spec.Replicas)
}
}
}
func TestHelmGetParams(t *testing.T) {
h := NewHelmApp("./testdata/redis")
params, err := h.GetParameters([]string{})
assert.Nil(t, err)
slaveCountParam := findParameter(params, "cluster.slaveCount")
assert.NotNil(t, slaveCountParam)
assert.Equal(t, slaveCountParam.Value, "1")
}
func TestHelmGetParamsValueFiles(t *testing.T) {
h := NewHelmApp("./testdata/redis")
params, err := h.GetParameters([]string{"values-production.yaml"})
assert.Nil(t, err)
slaveCountParam := findParameter(params, "cluster.slaveCount")
assert.NotNil(t, slaveCountParam)
assert.Equal(t, slaveCountParam.Value, "3")
}

View File

@@ -174,7 +174,11 @@ func GetCachedServerResources(host string, disco discovery.DiscoveryInterface) (
}
resList, err = disco.ServerResources()
if err != nil {
return nil, errors.WithStack(err)
if len(resList) == 0 {
return nil, errors.WithStack(err)
}
// It's possible for ServerResources to return error as well as a resource list
log.Warnf("Resource discovery partially successful. Encountered error: %v", err)
}
err = apiResourceCache.Set(&cache.Item{
Key: cacheKey,

View File

@@ -17,6 +17,7 @@ import (
"google.golang.org/grpc/status"
"github.com/argoproj/argo-cd/common"
jwtutil "github.com/argoproj/argo-cd/util/jwt"
passwordutil "github.com/argoproj/argo-cd/util/password"
"github.com/argoproj/argo-cd/util/settings"
)
@@ -166,26 +167,21 @@ func (mgr *SessionManager) VerifyToken(tokenString string) (jwt.Claims, error) {
}
}
func stringFromMap(input map[string]interface{}, key string) string {
if val, ok := input[key]; ok {
if res, ok := val.(string); ok {
return res
}
}
return ""
}
func Username(ctx context.Context) string {
if claims, ok := ctx.Value("claims").(*jwt.MapClaims); ok {
mapClaims := *claims
switch stringFromMap(mapClaims, "iss") {
case SessionManagerClaimsIssuer:
return stringFromMap(mapClaims, "sub")
default:
return stringFromMap(mapClaims, "email")
}
claims, ok := ctx.Value("claims").(jwt.Claims)
if !ok {
return ""
}
mapClaims, err := jwtutil.MapClaims(claims)
if err != nil {
return ""
}
switch jwtutil.GetField(mapClaims, "iss") {
case SessionManagerClaimsIssuer:
return jwtutil.GetField(mapClaims, "sub")
default:
return jwtutil.GetField(mapClaims, "email")
}
return ""
}
// MakeCookieMetadata generates a string representing a Web cookie. Yum!