Initial commit

Change-Id: I7e9a2194a2e83a04dc3e91b26978e063c3c9b6aa
Signed-off-by: Ruslan Aliev <raliev@mirantis.com>
This commit is contained in:
Ruslan Aliev 2024-01-29 10:43:21 -06:00
parent d2b43f3f1f
commit 828339c24e
22 changed files with 2371 additions and 0 deletions

26
.gitignore vendored Normal file
View File

@ -0,0 +1,26 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
bin/*
Dockerfile.cross
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Kubernetes Generated files - skip generated files, except for vendored files
!vendor/**/zz_generated.*
# editor and IDE paraphernalia
.idea
.vscode
*.swp
*.swo
*~

89
.zuul.yaml Normal file
View File

@ -0,0 +1,89 @@
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- project:
check:
jobs:
- armada-go-docker-build-gate-ubuntu_focal
gate:
jobs:
- armada-go-docker-build-gate-ubuntu_focal
post:
jobs:
- armada-go-docker-publish-ubuntu_focal
- nodeset:
name: armada-go-single-node-focal
nodes:
- name: primary
label: ubuntu-focal
- job:
name: armada-go-docker-build-gate-ubuntu_focal
timeout: 3600
run: tools/gate/playbooks/docker-image-build.yaml
nodeset: armada-go-single-node-focal
vars:
publish: false
distro: ubuntu_focal
tags:
dynamic:
patch_set: true
- job:
name: armada-go-docker-publish-ubuntu_focal
timeout: 3600
run: tools/gate/playbooks/docker-image-build.yaml
nodeset: armada-go-single-node-focal
secrets:
- airship_armada_go_quay_creds
vars:
publish: true
distro: ubuntu_focal
tags:
dynamic:
branch: true
commit: true
static:
- latest
- secret:
name: airship_armada_go_quay_creds
data:
username: !encrypted/pkcs1-oaep
- WT0T3twqEWlkYWkvqmuWvvU/cYXMVurCPjELm0ilZqVBBj9traoHaUgURfYiNJSu6+u+X
vSugo9uaPgCXM5oQbAyWmtwDH+WHVOmdflx+Q2usmaym7JEET+PYh7DEKsrf3/YRtotn3
3r8hEXFbaq0+k5MJdc+byQ6CX2PS2W4I8TTRbH+jMDEDttEAXNDpG1C63Geol9deo7c2u
eQXdzTVcyZ+fn0/nb8bMdAjC+KpWCpU6O2/kMYStdDQYr105JYD1e36YVeGheSDAHqHIj
1LDHqtjGMeiliimizZ08Lero/yjZiUpQCUGVYqyqi9Tpk5OmecJR9ub0W5pTBPmEMkbiN
R/8owZMczjWdhD7/Vej9rJAu8iHGnRMJoWYCtLD0De0q6OxQEpSWJl6c7aKsmRorAB3Jt
sM+GuVwEBllaj67l176Ql4T662zIBVaSUckcsVjQef+JMMj6CKgF+U43pylwFUk/ilnP5
HJfo8AUtW1P5hpU8xLB4mcS1h5K0ynneWIydiB2aUBsQ34e0T/u1OLfKiLdMBaaPS4Olu
ks16VnS1VDNXlt0hKEylS4IX77oxtXnRnFpV/3Z3ohlrccvT23FiUr2k0maFTsqQYBs2F
KJxMTtI/x9G4kk7FtZKyg2w6BYhEgOhmajcqksfEGSgytmhugVq6RC22hHROTM=
password: !encrypted/pkcs1-oaep
- bNJ9MY8MvMpwIsvGmY9GsZz1b9XCUeOAlpvL/KAXRGT7sBjt15nvobF+Y/RXjGia+kq/C
Y8Yz8DEwHAZ9EjqsY20uhoIC89ArjziGxq+ZFl89HHvmWkgs+TYuBh9A0MZUiAOJNkJR4
yQIWPYvfzknutFqZXGzQ2GPGvp+Lk45PZJs4io91+WQX1DT16fdWpB/CtVfZCrFOyLswM
58f+K56wf+I2tly9KoAp83gYVE+mGwkqqBUdv6jNcnm9/UMxTGFj1zgKNJdF0sSpT1T1I
DwynMkKDcvJCsUoH0S0EO/JYd2ne5VsKrI5/4EU2gmBrvvQFyCqccjYHc3557TN+bAe0H
6xtQSZfM31i/IiGBhR1fqQbyAWVmA27i5pmn5TcekI7YpELCeoIRY5M/BQ5slqpTvbp4C
VEez4iQvXoHepfRtpFN3/zATFolTqhCldJN+bh6wdg3d8lSecsetD6cU3Fm7aHJuLYoSr
a4xPhx6s7B1J6wq0P14ADGl5/0CmzeZ3uGw94PRGvg8YAWswUV/DXWogbOH/EEStdsOia
JaXpU/oVkmC5NcIp3A+6F3NCft8gQCTKecoFBt9/7suvjmCIY9y26SBQ0gOHEjbUzfikT
FuwYIUDaaLDU+Dz+PfdrKSM63O/uBhrDO+yo+ovqu5PPLksBu42sOrZleAbZIA=

201
LICENSE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

132
Makefile Normal file
View File

@ -0,0 +1,132 @@
# APP INFO
BUILD_DIR := $(shell mktemp -d)
DOCKER_REGISTRY ?= quay.io
IMAGE_PREFIX ?= airshipit
IMAGE_NAME ?= armada-go
IMAGE_TAG ?= latest
PROXY ?= http://proxy.foo.com:8000
NO_PROXY ?= localhost,127.0.0.1,.svc.cluster.local
USE_PROXY ?= false
PUSH_IMAGE ?= false
# use this variable for image labels added in internal build process
LABEL ?= org.airshipit.build=community
COMMIT ?= $(shell git rev-parse HEAD)
PYTHON = python3
CHARTS := $(filter-out deps, $(patsubst charts/%/.,%,$(wildcard charts/*/.)))
DISTRO ?= ubuntu_focal
IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${IMAGE_NAME}:${IMAGE_TAG}-${DISTRO}
UBUNTU_BASE_IMAGE ?=
# VERSION INFO
GIT_COMMIT = $(shell git rev-parse HEAD)
GIT_SHA = $(shell git rev-parse --short HEAD)
GIT_TAG = $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null)
GIT_DIRTY = $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean")
ifdef VERSION
DOCKER_VERSION = $(VERSION)
endif
SHELL = /bin/bash
info:
@echo "Version: ${VERSION}"
@echo "Git Tag: ${GIT_TAG}"
@echo "Git Commit: ${GIT_COMMIT}"
@echo "Git Tree State: ${GIT_DIRTY}"
@echo "Docker Version: ${DOCKER_VERSION}"
@echo "Registry: ${DOCKER_REGISTRY}"
# Image URL to use all building/pushing image targets
IMG ?= quay.io/airshipit/armada-go:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.28.0
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif
# CONTAINER_TOOL defines the container tool to be used for building images.
# Be aware that the target commands are only tested with Docker which is
# scaffolded by default. However, you might want to replace it to use other
# tools. (i.e. podman)
CONTAINER_TOOL ?= docker
# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
.PHONY: all
all: build
##@ General
# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk command is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php
.PHONY: help
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
##@ Build
.PHONY: build
build: ## Build manager binary.
go build -o bin/armada-go main.go
.PHONY: run
run: ## Run a controller from your host.
go run main.go
# If you wish to build the manager image targeting other platforms you can use the --platform flag.
# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it.
# More info: https://docs.docker.com/develop/develop-images/build_enhancements/
.PHONY: docker-build
docker-build: ## Build docker image with the manager.
$(CONTAINER_TOOL) build -t ${IMG} .
.PHONY: docker-push
docker-push: ## Push docker image with the manager.
$(CONTAINER_TOOL) push ${IMG}
check-docker:
@if [ -z $$(which docker) ]; then \
echo "Missing \`docker\` client which is required for development"; \
exit 2; \
fi
images: check-docker build_armada_go
_BASE_IMAGE_ARG := $(if $(UBUNTU_BASE_IMAGE),--build-arg FROM="${UBUNTU_BASE_IMAGE}" ,)
build_armada_go:
ifeq ($(USE_PROXY), true)
docker build --network host -t $(IMAGE) --label $(LABEL) \
--label "org.opencontainers.image.revision=$(COMMIT)" \
--label "org.opencontainers.image.created=$(shell date --rfc-3339=seconds --utc)" \
--label "org.opencontainers.image.title=$(IMAGE_NAME)" \
-f images/armada-go/Dockerfile.$(DISTRO) \
$(_BASE_IMAGE_ARG) \
--build-arg http_proxy=$(PROXY) \
--build-arg https_proxy=$(PROXY) \
--build-arg HTTP_PROXY=$(PROXY) \
--build-arg HTTPS_PROXY=$(PROXY) \
--build-arg no_proxy=$(NO_PROXY) \
--build-arg NO_PROXY=$(NO_PROXY) .
else
docker build --network host -t $(IMAGE) --label $(LABEL) \
--label "org.opencontainers.image.revision=$(COMMIT)" \
--label "org.opencontainers.image.created=$(shell date --rfc-3339=seconds --utc)" \
--label "org.opencontainers.image.title=$(IMAGE_NAME)" \
-f images/armada-go/Dockerfile.$(DISTRO) \
$(_BASE_IMAGE_ARG) \
--build-arg HELM_ARTIFACT_URL=$(HELM_ARTIFACT_URL) .
endif
ifeq ($(PUSH_IMAGE), true)
docker push $(IMAGE)
endif

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# armada-go
A Golang-based orchestrator for managing a collection of Kubernetes Helm charts

45
cmd/apply.go Normal file
View File

@ -0,0 +1,45 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"github.com/spf13/cobra"
"opendev.org/airship/armada-go/pkg/apply"
"opendev.org/airship/armada-go/pkg/config"
)
// NewApplyCommand creates a command to apply armada manifests
func NewApplyCommand(cfgFactory config.Factory) *cobra.Command {
p := &apply.RunCommand{Factory: cfgFactory}
runCmd := &cobra.Command{
Use: "apply",
Short: "armada-go command to apply manifests",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
p.Manifests = args[0]
p.Out = cmd.OutOrStdout()
return p.RunE()
},
}
var metricsOutput string
flags := runCmd.Flags()
flags.StringVar(&p.TargetManifest, "target-manifest", "", "target manifest")
flags.StringVar(&metricsOutput, "metrics-output", "", "metrics output")
return runCmd
}

81
cmd/root.go Normal file
View File

@ -0,0 +1,81 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"io"
"path/filepath"
"github.com/spf13/cobra"
cfg "opendev.org/airship/armada-go/pkg/config"
"opendev.org/airship/armada-go/pkg/log"
)
const longRoot = `Armada-Go is a tool for managing multiple Helm charts with dependencies by centralizing
all configurations in a single Armada YAML and providing life-cycle hooks for all Helm releases.`
// RootOptions stores global flags values
type RootOptions struct {
Debug bool
ArmadaConfigPath string
}
// NewArmadaCommand creates a root `armada` command with the default commands attached
func NewArmadaCommand(out io.Writer) *cobra.Command {
rootCmd, settings := NewRootCommand(out)
return AddDefaultArmadaCommands(rootCmd,
cfg.CreateFactory(&settings.ArmadaConfigPath))
}
// NewRootCommand creates the root `armada` command. All other commands are
// subcommands branching from this one
func NewRootCommand(out io.Writer) (*cobra.Command, *RootOptions) {
options := &RootOptions{}
rootCmd := &cobra.Command{
Use: "armada",
Short: "A Golang-based orchestrator for managing a collection of Kubernetes Helm charts",
Long: longRoot,
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
log.Init(options.Debug, cmd.ErrOrStderr())
},
}
rootCmd.SetOut(out)
initFlags(options, rootCmd)
return rootCmd, options
}
// AddDefaultArmadaCommands is a convenience function for adding all the
// default commands to armada-go
func AddDefaultArmadaCommands(cmd *cobra.Command, factory cfg.Factory) *cobra.Command {
cmd.AddCommand(NewServerCommand(factory))
cmd.AddCommand(NewApplyCommand(factory))
return cmd
}
func initFlags(options *RootOptions, cmd *cobra.Command) {
flags := cmd.PersistentFlags()
flags.BoolVar(&options.Debug, "debug", false, "enable verbose output")
defaultArmadaConfigDir := filepath.Join("$HOME", ".armada")
defaultArmadaConfigPath := filepath.Join(defaultArmadaConfigDir, "config")
flags.StringVar(&options.ArmadaConfigPath, "armadaconf", "",
`path to the armada-go configuration file. Defaults to "`+defaultArmadaConfigPath+`"`)
}

50
cmd/server.go Normal file
View File

@ -0,0 +1,50 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
import (
"github.com/spf13/cobra"
"opendev.org/airship/armada-go/pkg/config"
"opendev.org/airship/armada-go/pkg/server"
)
const (
runLong = `
Run armada-go in server mode
`
runExample = `
Run armada-go server
# armada-go server
`
)
// NewServerCommand creates a command to run specific phase
func NewServerCommand(cfgFactory config.Factory) *cobra.Command {
p := &server.RunCommand{Factory: cfgFactory}
runCmd := &cobra.Command{
Use: "server",
Short: "armada-go command to run server",
Long: runLong[1:],
Args: cobra.ExactArgs(0),
Example: runExample,
RunE: func(cmd *cobra.Command, args []string) error {
return p.RunE()
},
}
return runCmd
}

242
crd.yaml Normal file
View File

@ -0,0 +1,242 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.13.0
name: armadacharts.armada.airshipit.org
spec:
group: armada.airshipit.org
names:
kind: ArmadaChart
listKind: ArmadaChartList
plural: armadacharts
singular: armadachart
scope: Namespaced
versions:
- name: v1
schema:
openAPIV3Schema:
description: ArmadaChart is the Schema for the armadacharts API
properties:
apiVersion:
description: 'APIVersion defines the versioned schema of this representation
of an object. Servers should convert recognized schemas to the latest
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
type: string
data:
description: ArmadaChartSpec defines the specification of ArmadaChart
properties:
chart_name:
description: ChartName is name of ArmadaChart
type: string
namespace:
description: Namespace is a namespace for ArmadaChart
type: string
release:
description: Release is a name of corresponding Helm Release of ArmadaChart
type: string
source:
description: Source is a source location of Helm Chart *.tgz
properties:
location:
type: string
subpath:
type: string
type:
type: string
type: object
test:
description: Test holds the test parameters for this Helm release.
properties:
enabled:
description: Enabled is an example field of ArmadaChart. Edit
armadachart_types.go to remove/update
type: boolean
type: object
upgrade:
description: Upgrade holds the upgrade options for this Helm release.
properties:
pre:
properties:
delete:
items:
description: ArmadaChartDeleteResource defines the delete
options of ArmadaChart
properties:
labels:
additionalProperties:
type: string
type: object
type:
type: string
type: object
type: array
type: object
type: object
values:
description: Values holds the values for this Helm release.
x-kubernetes-preserve-unknown-fields: true
wait:
description: Wait holds the wait options for this Helm release.
properties:
labels:
additionalProperties:
type: string
type: object
native:
description: ArmadaChartWaitNative defines the wait options of
ArmadaChart
properties:
enabled:
type: boolean
type: object
resources:
items:
description: ArmadaChartWaitResource defines the wait options
of ArmadaChart
properties:
labels:
additionalProperties:
type: string
type: object
min_ready:
type: string
type:
type: string
type: object
type: array
timeout:
description: Timeout is the time to wait for full reconciliation
of Helm release.
type: integer
type: object
type: object
kind:
description: 'Kind is a string value representing the REST resource this
object represents. Servers may infer this from the endpoint the client
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
type: string
metadata:
type: object
status:
description: ArmadaChartStatus defines the observed state of ArmadaChart
properties:
conditions:
description: Conditions holds the conditions for the ArmadaChart.
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
failures:
description: Failures is the reconciliation failure count against
the latest desired state. It is reset after a successful reconciliation.
format: int64
type: integer
helmChart:
description: HelmChart is the namespaced name of the HelmChart resource
created by the controller for the ArmadaChart.
type: string
installFailures:
description: InstallFailures is the install failure count against
the latest desired state. It is reset after a successful reconciliation.
format: int64
type: integer
lastAppliedRevision:
description: LastAppliedRevision is the revision of the last successfully
applied source.
type: string
lastAttemptedRevision:
description: LastAttemptedRevision is the revision of the last reconciliation
attempt.
type: string
lastAttemptedValuesChecksum:
description: LastAttemptedValuesChecksum is the SHA1 checksum of the
values of the last reconciliation attempt.
type: string
lastReleaseRevision:
description: LastReleaseRevision is the revision of the last successful
Helm release.
type: integer
observedGeneration:
description: ObservedGeneration is the last observed generation.
format: int64
type: integer
tested:
description: Tested is the bool value whether the Helm Release was
successfully tested or not.
type: boolean
upgradeFailures:
description: UpgradeFailures is the upgrade failure count against
the latest desired state. It is reset after a successful reconciliation.
format: int64
type: integer
type: object
type: object
served: true
storage: true
subresources:
status: {}

72
go.mod Normal file
View File

@ -0,0 +1,72 @@
module opendev.org/airship/armada-go
go 1.20
require (
github.com/gin-gonic/gin v1.9.1
github.com/go-logr/logr v1.2.4
github.com/spf13/cobra v1.7.0
golang.org/x/sync v0.3.0
k8s.io/api v0.28.4
k8s.io/apiextensions-apiserver v0.28.3
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/klog/v2 v2.100.1
opendev.org/airship/armada-operator v0.0.0-20240131234736-165edb913367
sigs.k8s.io/yaml v1.4.0
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/go-openapi/swag v0.22.3 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect
k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect
sigs.k8s.io/controller-runtime v0.16.3 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect
)

211
go.sum Normal file
View File

@ -0,0 +1,211 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.28.4 h1:8ZBrLjwosLl/NYgv1P7EQLqoO8MGQApnbgH8tu3BMzY=
k8s.io/api v0.28.4/go.mod h1:axWTGrY88s/5YE+JSt4uUi6NMM+gur1en2REMR7IRj0=
k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08=
k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc=
k8s.io/apimachinery v0.28.4 h1:zOSJe1mc+GxuMnFzD4Z/U1wst50X28ZNsn5bhgIIao8=
k8s.io/apimachinery v0.28.4/go.mod h1:wI37ncBvfAoswfq626yPTe6Bz1c22L7uaJ8dho83mgg=
k8s.io/client-go v0.28.4 h1:Np5ocjlZcTrkyRJ3+T3PkXDpe4UpatQxj85+xjaD2wY=
k8s.io/client-go v0.28.4/go.mod h1:0VDZFpgoZfelyP5Wqu0/r/TRYcLYuJ2U1KEeoaPa1N4=
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ=
k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM=
k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU=
k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
opendev.org/airship/armada-operator v0.0.0-20240131234736-165edb913367 h1:Ae/TMu6lXzpF4KlLGpshu6oL9BZ8OwYYNGNvnLyBnVw=
opendev.org/airship/armada-operator v0.0.0-20240131234736-165edb913367/go.mod h1:0hmfND6t5ZuyHB3R7TmsAreqzF4vfc/q3LwX18gFldI=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4=
sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@ -0,0 +1,38 @@
ARG FROM=ubuntu:20.04
ARG GO_IMAGE=golang:1.20.5-buster
FROM ${GO_IMAGE} as builder
ENV PATH "/usr/local/go/bin:$PATH"
ENV CGO_ENABLED=0
WORKDIR /go/src/
COPY go.mod /go.sum ./
RUN go mod download
COPY . ./
RUN go build -v -o /usr/local/bin/armada-go ./
FROM ${FROM} as release
LABEL org.opencontainers.image.authors='airship-discuss@lists.airshipit.org, irc://#airshipit@freenode' \
org.opencontainers.image.url='https://airshipit.org' \
org.opencontainers.image.documentation='https://docs.airshipit.org/armada-go' \
org.opencontainers.image.source='https://opendev.org/airship/armada-go' \
org.opencontainers.image.vendor='The Airship Authors' \
org.opencontainers.image.licenses='Apache-2.0'
ENV DEBIAN_FRONTEND noninteractive
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
EXPOSE 8000
WORKDIR /armada
COPY --from=builder /usr/local/bin/armada-go /usr/local-bin/armada
COPY crd.yaml /armada/crd.yaml
# Add armada user
RUN useradd -u 1000 -g users -d $(pwd) armada
ENTRYPOINT ["/usr/local/bin/armada"]
CMD ["server"]
USER armada

29
main.go Normal file
View File

@ -0,0 +1,29 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package main
import (
"fmt"
"os"
"opendev.org/airship/armada-go/cmd"
)
func main() {
if err := cmd.NewArmadaCommand(os.Stdout).Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

383
pkg/apply/apply.go Normal file
View File

@ -0,0 +1,383 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apply
import (
"bufio"
"context"
"errors"
"flag"
"fmt"
"io"
"os"
"strings"
"time"
"golang.org/x/sync/errgroup"
v1 "k8s.io/api/core/v1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextension "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/klog/v2"
"sigs.k8s.io/yaml"
"opendev.org/airship/armada-go/pkg/config"
armadawait "opendev.org/airship/armada-go/pkg/wait"
armadav1 "opendev.org/airship/armada-operator/api/v1"
)
// RunCommand phase run command
type RunCommand struct {
Factory config.Factory
Manifests string
TargetManifest string
Out io.Writer
airManifest *AirshipManifest
airGroups map[string]*AirshipChartGroup
airCharts map[string]*AirshipChart
}
type AirshipDocument struct {
Schema string `json:"schema,omitempty"`
Metadata AirshipMetadata `json:"metadata,omitempty"`
}
type AirshipMetadata struct {
Name string `json:"name,omitempty"`
}
type AirshipManifest struct {
AirshipDocument
AirshipManifestSpec `json:"data,omitempty"`
}
type AirshipManifestSpec struct {
ChartGroups []string `json:"chart_groups,omitempty"`
ReleasePrefix string `json:"release_prefix,omitempty"`
}
type AirshipChartGroup struct {
AirshipDocument
AirshipChartGroupSpec `json:"data,omitempty"`
}
type AirshipChartGroupSpec struct {
ChartGroup []string `json:"chart_group,omitempty"`
Description string `json:"description,omitempty"`
Sequenced bool `json:"sequenced,omitempty"`
}
type AirshipChart struct {
AirshipDocument
armadav1.ArmadaChartSpec `json:"data,omitempty"`
}
// RunE runs the phase
func (c *RunCommand) RunE() error {
klog.InitFlags(nil)
klog.SetOutput(c.Out)
if err := flag.Set("v", "5"); err != nil {
return err
}
klog.V(2).Infof("armada-go apply, manifests path %s", c.Manifests)
if err := c.ParseManifests(); err != nil {
return err
}
k8sConfig, err := rest.InClusterConfig()
if err != nil {
klog.V(2).Infoln("Unable to load in-cluster kubeconfig, reason: ", err)
k8sConfig, err = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).ClientConfig()
if err != nil {
return err
}
}
if err := c.VerifyNamespaces(k8sConfig); err != nil {
return err
}
dc := dynamic.NewForConfigOrDie(k8sConfig)
resClient := dc.Resource(schema.GroupVersionResource{
Group: armadav1.ArmadaChartGroup,
Version: armadav1.ArmadaChartVersion,
Resource: armadav1.ArmadaChartPlural,
})
acClient := armadav1.NewForConfigOrDie(k8sConfig)
if err := c.CheckCRD(k8sConfig); err != nil {
return err
}
for _, cgName := range c.airManifest.ChartGroups {
cg := c.airGroups[cgName]
klog.V(5).Infof("processing chart group %s, sequenced %s", cgName, cg.Sequenced)
if !cg.Sequenced {
eg := errgroup.Group{}
for _, cName := range cg.ChartGroup {
klog.V(5).Infof("adding 1 chart to wg %s", cName)
chp := c.airCharts[cName]
chpc := c.ConvertChart(chp)
eg.Go(func() error {
return c.InstallChart(chpc, resClient, acClient)
})
}
if err := eg.Wait(); err != nil {
return err
}
} else {
for _, cName := range cg.ChartGroup {
klog.V(5).Infof("sequential chart install %s", cName)
if err = c.InstallChart(c.ConvertChart(c.airCharts[cName]), resClient, acClient); err != nil {
return err
}
}
}
}
return nil
}
func (c *RunCommand) InstallChart(
chart *armadav1.ArmadaChart,
resClient dynamic.NamespaceableResourceInterface,
restConfig *rest.RESTClient) error {
klog.V(5).Infof("installing chart %s %s %s", chart.GetName(), chart.Name, chart.Namespace)
obj, err := runtime.DefaultUnstructuredConverter.ToUnstructured(chart)
if err != nil {
return err
}
if oldObj, err := resClient.Namespace(chart.Namespace).Get(
context.Background(), chart.GetName(), metav1.GetOptions{}); err != nil {
klog.V(5).Infof("unable to get chart %s: %s, creating", chart.Name, err.Error())
if _, err = resClient.Namespace(chart.Namespace).Create(
context.Background(), &unstructured.Unstructured{Object: obj}, metav1.CreateOptions{}); err != nil {
return err
}
klog.V(5).Infof("chart has been successfully created %s", chart.Name)
} else {
uObj := &unstructured.Unstructured{Object: obj}
uObj.SetResourceVersion(oldObj.GetResourceVersion())
klog.V(5).Infof("chart %s was found, updating", chart.Name)
if _, err = resClient.Namespace(chart.Namespace).Update(
context.Background(), uObj, metav1.UpdateOptions{}); err != nil {
klog.V(5).Infof("resource update error: %s", err.Error())
if strings.Contains(err.Error(), "the object has been modified") {
klog.V(5).Infof("resource expired, retrying %s", err.Error())
return c.InstallChart(chart, resClient, restConfig)
}
return err
}
klog.V(5).Infof("chart has been successfully updated %s", chart.Name)
}
wOpts := armadawait.WaitOptions{
Getter: restConfig,
Namespace: chart.Namespace,
LabelSelector: fmt.Sprintf("%s=%s", armadav1.ArmadaChartLabel,
fmt.Sprintf("%s-%s", c.airManifest.ReleasePrefix, chart.Spec.Release)),
ResourceType: "armadacharts.armada.airshipit.io",
Timeout: time.Second * time.Duration(chart.Spec.Wait.Timeout),
Logger: klog.FromContext(context.Background()),
}
err = wOpts.Wait(context.Background())
klog.V(5).Infof("finished with chart %s", chart.GetName())
return err
}
func (c *RunCommand) ConvertChart(chart *AirshipChart) *armadav1.ArmadaChart {
return &armadav1.ArmadaChart{
TypeMeta: metav1.TypeMeta{
Kind: armadav1.ArmadaChartKind,
APIVersion: armadav1.ArmadaChartAPIVersion,
},
ObjectMeta: metav1.ObjectMeta{
Name: fmt.Sprintf("%s-%s", c.airManifest.ReleasePrefix, chart.Release),
Namespace: chart.Namespace,
Labels: map[string]string{
armadav1.ArmadaChartLabel: fmt.Sprintf("%s-%s", c.airManifest.ReleasePrefix, chart.Release),
},
},
Spec: chart.ArmadaChartSpec,
}
}
func (c *RunCommand) CheckCRD(restConfig *rest.Config) error {
crdClient := apiextension.NewForConfigOrDie(restConfig)
if _, err := crdClient.ApiextensionsV1().CustomResourceDefinitions().Get(context.Background(), "armadacharts.armada.airshipit.io", metav1.GetOptions{}); err != nil {
if apierrors.IsNotFound(err) {
klog.V(5).Infof("armadacharts CRD not found, creating: %s", err.Error())
objToapp, err := c.ReadCRD()
if err != nil {
return err
}
_, err = crdClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), objToapp, metav1.CreateOptions{})
if err != nil {
klog.V(5).Infof("error while creating crd %t", err)
return err
}
} else {
return err
}
}
return nil
}
func (c *RunCommand) ReadCRD() (*apiextv1.CustomResourceDefinition, error) {
sch := runtime.NewScheme()
_ = scheme.AddToScheme(sch)
_ = apiextv1.AddToScheme(sch)
decode := serializer.NewCodecFactory(sch).UniversalDeserializer().Decode
data, err := os.ReadFile("crd.yaml")
if err != nil {
return nil, err
}
obj, _, err := decode(data, nil, nil)
if err != nil {
return nil, err
}
crdTo := obj.(*apiextv1.CustomResourceDefinition)
return crdTo, nil
}
func (c *RunCommand) VerifyNamespaces(rsc *rest.Config) error {
cs := kubernetes.NewForConfigOrDie(rsc)
namespaces := make(map[string]bool)
for _, cgname := range c.airManifest.ChartGroups {
cg := c.airGroups[cgname]
for _, chrt := range cg.ChartGroup {
ns := c.airCharts[chrt].Namespace
if _, ok := namespaces[ns]; !ok {
namespaces[ns] = true
}
}
}
for k, _ := range namespaces {
klog.V(5).Infof("processing namespace %s", k)
if _, err := cs.CoreV1().Namespaces().Get(context.Background(), k, metav1.GetOptions{}); err != nil {
if apierrors.IsNotFound(err) {
klog.V(5).Infof("namespace %s not found, creating", k)
if _, err = cs.CoreV1().Namespaces().Create(context.Background(), &v1.Namespace{
ObjectMeta: metav1.ObjectMeta{Name: k}}, metav1.CreateOptions{}); err != nil {
return err
}
} else {
return err
}
}
}
klog.V(5).Infof("all namespaces validated successfully")
return nil
}
func (c *RunCommand) ValidateManifests() error {
if c.airManifest == nil {
return errors.New("no or multiple armada manifest found")
}
for _, cgname := range c.airManifest.ChartGroups {
if cg, ok := c.airGroups[cgname]; ok {
for _, cName := range cg.ChartGroup {
if chrt, ok := c.airCharts[cName]; ok {
if chrt.Release == "" || chrt.Namespace == "" {
return errors.New(fmt.Sprintf("chart document with name %s found does not have release or ns", cName))
}
} else {
return errors.New(fmt.Sprintf("no chart document with name %s found", cName))
}
}
} else {
return errors.New(fmt.Sprintf("no group document with name %s found", cgname))
}
}
klog.V(5).Infof("all airship manifests validated successfully")
return nil
}
func (c *RunCommand) ParseManifests() error {
klog.V(5).Infof("parsing manifests started, path: %s", c.Manifests)
f, err := os.Open(c.Manifests)
if err != nil {
return err
}
defer f.Close()
c.airCharts = map[string]*AirshipChart{}
c.airGroups = map[string]*AirshipChartGroup{}
multidocReader := utilyaml.NewYAMLReader(bufio.NewReader(f))
for {
buf, err := multidocReader.Read()
if err != nil {
if err == io.EOF {
break
}
return err
}
var typeMeta AirshipDocument
if err := yaml.Unmarshal(buf, &typeMeta); err != nil {
klog.V(2).Infof("unmarshalling error %s, continuing...", err.Error())
continue
}
if typeMeta.Schema == "armada/Manifest/v1" {
if (c.TargetManifest != "" && typeMeta.Metadata.Name == c.TargetManifest) ||
(c.TargetManifest == "" && c.airManifest == nil) {
var airManifest AirshipManifest
if err := yaml.Unmarshal(buf, &airManifest); err != nil {
return err
}
klog.V(2).Infof("found airship manifest %s", airManifest.Metadata.Name)
c.airManifest = &airManifest
}
}
if typeMeta.Schema == "armada/ChartGroup/v1" {
var cg AirshipChartGroup
if err := yaml.Unmarshal(buf, &cg); err != nil {
return err
}
c.airGroups[typeMeta.Metadata.Name] = &cg
}
if typeMeta.Schema == "armada/Chart/v1" {
var chrt AirshipChart
if err := yaml.Unmarshal(buf, &chrt); err != nil {
return err
}
c.airCharts[typeMeta.Metadata.Name] = &chrt
}
}
return c.ValidateManifests()
}

169
pkg/config/config.go Normal file
View File

@ -0,0 +1,169 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config
import (
"fmt"
"os"
"path/filepath"
"sigs.k8s.io/yaml"
"opendev.org/airship/armada-go/pkg/log"
"opendev.org/airship/armada-go/pkg/util"
)
// Where possible, json tags match the cli argument names.
// Top level config objects and all values required for proper functioning are not "omitempty".
// Any truly optional piece of config is allowed to be omitted.
// Config holds the information required by armada-go commands
// It is somewhat a superset of what a kubeconfig looks like
type Config struct {
// +optional
Kind string `json:"kind,omitempty"`
// loadedConfigPath is the full path to the location of the config
// file from which this config was loaded
// +not persisted in file
loadedConfigPath string
//fileSystem kustfs.FileSystem
}
// Factory is a function which returns ready to use config object and error (if any)
type Factory func() (*Config, error)
// CreateFactory returns function which creates ready to use Config object
func CreateFactory(armadaConfigPath *string) Factory {
return func() (*Config, error) {
cfg := NewEmptyConfig()
var acp string
if armadaConfigPath != nil {
acp = *armadaConfigPath
}
cfg.initConfigPath(acp)
err := cfg.LoadConfig()
if err != nil {
// Should stop armada-go
log.Print("Failed to load or initialize config: ", err)
CreateConfig(acp, true)
}
return cfg, nil
}
}
// CreateConfig saves default config to the specified path
func CreateConfig(armadaConfigPath string, overwrite bool) error {
cfg := NewConfig()
cfg.initConfigPath(armadaConfigPath)
return cfg.PersistConfig(overwrite)
}
// initConfigPath - Initializes loadedConfigPath variable for Config object
func (c *Config) initConfigPath(armadaConfigPath string) {
switch {
case armadaConfigPath != "":
// The loadedConfigPath may already have been received as a command line argument
c.loadedConfigPath = armadaConfigPath
case os.Getenv("ARMADA_CONFIG") != "":
// Otherwise, we can check if we got the path via ENVIRONMENT variable
c.loadedConfigPath = os.Getenv("ARMADA_CONFIG")
default:
// Otherwise, we'll try putting it in the home directory
c.loadedConfigPath = filepath.Join(util.UserHomeDir(), ".armada", "config")
}
}
func (c *Config) LoadConfig() error {
// If I can read from the file, load from it
// throw an error otherwise
data, err := os.ReadFile(c.loadedConfigPath)
if err != nil {
return err
}
return yaml.Unmarshal(data, c)
}
// NewEmptyConfig returns an initialized Config object with no default values
func NewEmptyConfig() *Config {
return &Config{}
}
// NewConfig returns a newly initialized Config object
func NewConfig() *Config {
return &Config{
Kind: "kind",
//fileSystem: kustfs.MakeFsInMemory(),
}
}
// ErrConfigFileExists is returned when there is an existing file at specified location
type ErrConfigFileExists struct {
Path string
}
func (e ErrConfigFileExists) Error() string {
return fmt.Sprintf("could not create default config at %s, file already exists", e.Path)
}
// ToYaml returns a YAML document
// It serializes the given Config object to a valid YAML document
func (c *Config) ToYaml() ([]byte, error) {
return yaml.Marshal(&c)
}
// PersistConfig updates the airshipctl config file to match
// the current Config object.
// If file did not previously exist, the file will be created.
// The file will be overwritten if overwrite argument set to true
func (c *Config) PersistConfig(overwrite bool) error {
if _, err := os.Stat(c.loadedConfigPath); err == nil && !overwrite {
return ErrConfigFileExists{Path: c.loadedConfigPath}
}
airshipConfigYaml, err := c.ToYaml()
if err != nil {
return err
}
// WriteFile doesn't create the directory, create it if needed
dir := filepath.Dir(c.loadedConfigPath)
err = os.MkdirAll(dir, 0755)
if err != nil {
return err
}
// Change the permission of directory
err = os.Chmod(dir, os.FileMode(0755))
if err != nil {
return err
}
// Write the Airship Config file
err = os.WriteFile(c.loadedConfigPath, airshipConfigYaml, 0644)
if err != nil {
return err
}
// Change the permission of config file
err = os.Chmod(c.loadedConfigPath, os.FileMode(0644))
if err != nil {
return err
}
return nil
}

92
pkg/log/log.go Normal file
View File

@ -0,0 +1,92 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package log
import (
"fmt"
"io"
"log"
"os"
)
var (
debug = false
armadaLog = log.New(os.Stderr, "[armada-go] ", log.LstdFlags)
)
// Init initializes settings related to logging
func Init(debugFlag bool, out io.Writer) {
debug = debugFlag
if debug {
armadaLog.SetFlags(log.LstdFlags | log.Llongfile)
}
armadaLog.SetOutput(out)
}
// DebugEnabled returns whether the debug level is set
func DebugEnabled() bool {
return debug
}
// Debug is a wrapper for log.Debug
func Debug(v ...interface{}) {
if debug {
writeLog(v...)
}
}
// Debugf is a wrapper for log.Debugf
func Debugf(format string, v ...interface{}) {
if debug {
writeLog(fmt.Sprintf(format, v...))
}
}
// Print is a wrapper for log.Print
func Print(v ...interface{}) {
writeLog(v...)
}
// Printf is a wrapper for log.Printf
func Printf(format string, v ...interface{}) {
writeLog(fmt.Sprintf(format, v...))
}
// Fatal is a wrapper for log.Fatal
func Fatal(v ...interface{}) {
armadaLog.Fatal(v...)
}
// Fatalf is a wrapper for log.Fatalf
func Fatalf(format string, v ...interface{}) {
armadaLog.Fatalf(format, v...)
}
// Writer returns log output writer object
func Writer() io.Writer {
return armadaLog.Writer()
}
func writeLog(v ...interface{}) {
if debug {
err := armadaLog.Output(3, fmt.Sprint(v...))
if err != nil {
log.Print(v...)
log.Print(err)
}
} else {
armadaLog.Print(v...)
}
}

42
pkg/server/server.go Normal file
View File

@ -0,0 +1,42 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"github.com/gin-gonic/gin"
"opendev.org/airship/armada-go/pkg/config"
"opendev.org/airship/armada-go/pkg/log"
)
// RunCommand phase run command
type RunCommand struct {
Factory config.Factory
}
func Apply(c *gin.Context) {
}
// RunE runs the phase
func (c *RunCommand) RunE() error {
_, err := c.Factory()
if err != nil {
return err
}
log.Printf("armada-go server has been started")
r := gin.Default()
r.GET("/apply", Apply)
return r.Run(":8000")
}

49
pkg/util/homedir.go Normal file
View File

@ -0,0 +1,49 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package util
import (
"os"
"path/filepath"
"strings"
)
// UserHomeDir is a utility function that wraps os.UserHomeDir and returns no
// errors. If the user has no home directory, the returned value will be the
// empty string
func UserHomeDir() string {
homeDir, err := os.UserHomeDir()
if err != nil {
homeDir = ""
}
return homeDir
}
// ExpandTilde expands the path to include the home directory if the path
// is prefixed with `~`. If it isn't prefixed with `~` or has no slash after tilde,
// the path is returned as-is.
func ExpandTilde(path string) string {
// Just tilde - return current $HOME dir
if path == "~" {
return UserHomeDir()
}
// If path starts with ~/ - expand it
if strings.HasPrefix(path, "~/") {
return filepath.Join(UserHomeDir(), path[1:])
}
// empty strings, absolute paths, ~<(dir/file)name> return as-is
return path
}

147
pkg/wait/wait.go Normal file
View File

@ -0,0 +1,147 @@
/*
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package wait
import (
"context"
"errors"
"fmt"
"time"
"github.com/go-logr/logr"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/tools/cache"
watchtools "k8s.io/client-go/tools/watch"
armadav1 "opendev.org/airship/armada-operator/api/v1"
)
type StatusType string
type Status struct {
StatusType
Msg string
}
const (
Ready StatusType = "READY"
Skipped StatusType = "SKIPPED"
Unready StatusType = "UNREADY"
Error StatusType = "ERROR"
)
// WaitOptions phase run command
type WaitOptions struct {
Getter cache.Getter
Namespace string
LabelSelector string
ResourceType string
Timeout time.Duration
Logger logr.Logger
}
func getObjectStatus(obj interface{}) Status {
switch v := obj.(type) {
case *armadav1.ArmadaChart:
return isArmadaChartReady(v)
default:
return Status{Error, fmt.Sprintf("Unable to cast an object to any type %s\n", obj)}
}
}
func allMatch(logger logr.Logger, store cache.Store, obj runtime.Object) (bool, error) {
for _, item := range store.List() {
if obj != nil && item == obj {
continue
}
status := getObjectStatus(item)
logger.Info(fmt.Sprintf("all match object %T is ready returned %s\n", item, status.StatusType))
logger.Info(status.Msg)
if status.StatusType != Ready && status.StatusType != Skipped {
logger.Info(fmt.Sprintf("all match exiting false due to %s\n", status.StatusType))
return false, nil
}
}
logger.Info("all objects are ready\n")
return true, nil
}
func processEvent(logger logr.Logger, event watch.Event) (StatusType, error) {
metaObj, err := meta.Accessor(event.Object)
if err != nil {
return Error, err
}
logger.Info(fmt.Sprintf("watch event: type=%s, name=%s, namespace=%s, resource_ver %s",
event.Type, metaObj.GetName(), metaObj.GetNamespace(), metaObj.GetResourceVersion()))
if event.Type == "ERROR" {
return Error, errors.New(fmt.Sprintf("resource %s: got error event %s", metaObj.GetName(), event.Object))
}
status := getObjectStatus(event.Object)
logger.Info(fmt.Sprintf("object type: %T, status: %s", event.Object, status.Msg))
return status.StatusType, nil
}
func isArmadaChartReady(ac *armadav1.ArmadaChart) Status {
if ac.Status.ObservedGeneration == ac.Generation {
for _, cond := range ac.Status.Conditions {
if cond.Type == "Ready" && cond.Status == "True" {
return Status{Ready, fmt.Sprintf("armadachart %s ready", ac.GetName())}
}
}
}
return Status{Unready, fmt.Sprintf("Waiting for armadachart %s to be ready", ac.GetName())}
}
// Wait runs the phase
func (c *WaitOptions) Wait(parent context.Context) error {
c.Logger.Info(fmt.Sprintf("armada-go wait , namespace %s labels %s type %s timeout %s", c.Namespace, c.LabelSelector, c.ResourceType, c.Timeout))
ctx, cancelFunc := watchtools.ContextWithOptionalTimeout(parent, c.Timeout)
defer cancelFunc()
lw := cache.NewFilteredListWatchFromClient(c.Getter, "armadacharts", c.Namespace, func(options *metav1.ListOptions) {
options.LabelSelector = c.LabelSelector
c.Logger.Info(fmt.Sprintf("Label selector applied %s", options))
})
var cacheStore cache.Store
cpu := func(store cache.Store) (bool, error) {
cacheStore = store
if len(store.List()) == 0 {
c.Logger.Info(fmt.Sprintf("skipping non-required wait, no resources found.\n"))
return true, nil
}
return allMatch(c.Logger, cacheStore, nil)
}
cfu := func(event watch.Event) (bool, error) {
if ready, err := processEvent(c.Logger, event); ready != Ready || err != nil {
return false, err
}
return allMatch(c.Logger, cacheStore, event.Object)
}
_, err := watchtools.UntilWithSync(ctx, lw, nil, cpu, cfu)
c.Logger.Info(fmt.Sprintf("wait completed %s\n", c.LabelSelector))
return err
}

View File

@ -0,0 +1,126 @@
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
- hosts: primary
roles:
- bindep
- ensure-docker
- ensure-python
- ensure-pip
tasks:
- include_vars: vars.yaml
- name: Debug tag generation inputs
block:
- debug:
var: publish
- debug:
var: distro
- debug:
var: tags
- debug:
var: zuul
- debug:
msg: "{{ tags | to_json }}"
- name: Determine tags
shell: echo '{{ tags | to_json }}' | python3 {{ zuul.project.src_dir }}/tools/image_tags.py
environment:
BRANCH: "{{ zuul.branch | default('') }}"
CHANGE: "{{ zuul.change | default('') }}"
COMMIT: "{{ zuul.newrev | default('') }}"
PATCHSET: "{{ zuul.patchset | default('') }}"
register: image_tags
- name: Debug computed tags
debug:
var: image_tags
- name: Install Docker python module for ansible docker login
block:
- pip:
name: docker
version: 4.4.4
executable: pip3
become: True
- name: Install tox python module for ansible docker login
block:
- pip:
name: tox
version: 3.28.0
executable: pip3
become: True
# - name: Run images
# when: not publish
# shell: |
# set -ex
# sudo -E -H pip3 install tox==3.28.0
# make run_images
# args:
# chdir: "{{ zuul.project.src_dir }}"
# executable: /bin/bash
# become: True
- name: Make images
when: not publish
block:
- make:
chdir: "{{ zuul.project.src_dir }}"
target: images
params:
DISTRO: "{{ distro }}"
IMAGE_TAG: "{{ item }}"
with_items: "{{ image_tags.stdout_lines }}"
- shell: "docker images"
register: docker_images
- debug:
var: docker_images
become: True
- name: Publish images
block:
- docker_login:
username: "{{ airship_armada_go_quay_creds.username }}"
password: "{{ airship_armada_go_quay_creds.password }}"
registry_url: "https://quay.io/api/v1/"
- make:
chdir: "{{ zuul.project.src_dir }}"
target: images
params:
DOCKER_REGISTRY: "quay.io"
IMAGE_PREFIX: "airshipit"
DISTRO: "{{ distro }}"
IMAGE_TAG: "{{ item }}"
COMMIT: "{{ zuul.newrev | default('') }}"
PUSH_IMAGE: "true"
with_items: "{{ image_tags.stdout_lines }}"
- shell: "docker images"
register: docker_images
- debug:
var: docker_images
when: publish
become: True

View File

@ -0,0 +1,19 @@
# Copyright 2017 The Openstack-Helm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
docker_daemon:
group: zuul
registry-mirrors:
- "http://{{ zuul_site_mirror_fqdn }}:8082/"
storage-driver: overlay2

126
tools/image_tags.py Normal file
View File

@ -0,0 +1,126 @@
#!/usr/bin/python3
# Copyright 2018 AT&T Intellectual Property. All other rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
import logging
import os
import sys
LOG = logging.getLogger(__name__)
LOG_FORMAT = '%(asctime)s %(levelname)-8s %(name)s:%(filename)s:%(lineno)3d:%(funcName)s %(message)s' # noqa
class TagGenExeception(Exception):
pass
def read_config(stream, env):
config = {}
try:
config['tags'] = json.load(stream)
except ValueError:
LOG.exception('Failed to decode JSON from input stream')
config['tags'] = {}
LOG.debug('Configuration after reading stream: %s', config)
config['context'] = {
'branch': env.get('BRANCH'),
'change': env.get('CHANGE'),
'commit': env.get('COMMIT'),
'ps': env.get('PATCHSET'),
}
LOG.info('Final configuration: %s', config)
return config
def build_tags(config):
tags = config.get('tags', {}).get('static', [])
LOG.debug('Dynamic tags: %s', tags)
tags.extend(build_dynamic_tags(config))
LOG.info('All tags: %s', tags)
return tags
def build_dynamic_tags(config):
dynamic_tags = []
dynamic_tags.extend(_build_branch_tag(config))
dynamic_tags.extend(_build_commit_tag(config))
dynamic_tags.extend(_build_ps_tag(config))
return dynamic_tags
def _build_branch_tag(config):
if _valid_dg(config, 'branch'):
return [config['context']['branch']]
else:
return []
def _build_commit_tag(config):
if _valid_dg(config, 'commit'):
return [config['context']['commit']]
else:
return []
def _build_ps_tag(config):
if _valid_dg(config, 'patch_set', 'change') and _valid_dg(
config, 'patch_set', 'ps'):
return [
'%s-%s' % (config['context']['change'], config['context']['ps'])
]
else:
return []
def _valid_dg(config, dynamic_tag, context_name=None):
if context_name is None:
context_name = dynamic_tag
if config.get('tags', {}).get('dynamic', {}).get(dynamic_tag):
if config.get('context', {}).get(context_name):
return True
else:
raise TagGenExeception('Dynamic tag "%s" requested, but "%s"'
' not found in context' % (dynamic_tag,
context_name))
else:
return False
def main():
config = read_config(sys.stdin, os.environ)
tags = build_tags(config)
for tag in tags:
print(tag)
if __name__ == '__main__':
logging.basicConfig(format=LOG_FORMAT, level=logging.WARNING)
try:
main()
except TagGenExeception:
LOG.exception('Failed to generate tags')
sys.exit(1)
except Exception:
LOG.exception('Unexpected exception')
sys.exit(2)